Merge branch 'more-efficient-ci' into 'develop'
authorlain <lain@soykaf.club>
Thu, 30 Dec 2021 15:53:33 +0000 (15:53 +0000)
committerlain <lain@soykaf.club>
Thu, 30 Dec 2021 15:53:33 +0000 (15:53 +0000)
CI: Use own package as base

See merge request pleroma/pleroma!3577

30 files changed:
CHANGELOG.md
config/config.exs
config/description.exs
lib/pleroma/application.ex
lib/pleroma/reverse_proxy/client/wrapper.ex
lib/pleroma/telemetry/logger.ex
lib/pleroma/web/api_spec/operations/account_operation.ex
lib/pleroma/web/api_spec/operations/app_operation.ex
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex [new file with mode: 0644]
lib/pleroma/web/api_spec/schemas/app.ex [new file with mode: 0644]
lib/pleroma/web/mastodon_api/controllers/account_controller.ex
lib/pleroma/web/mastodon_api/controllers/app_controller.ex
lib/pleroma/web/mastodon_api/views/instance_view.ex
lib/pleroma/web/nodeinfo/nodeinfo.ex
lib/pleroma/web/o_auth/app.ex
lib/pleroma/web/pleroma_api/controllers/app_controller.ex [new file with mode: 0644]
lib/pleroma/web/pleroma_api/views/app_view.ex [new file with mode: 0644]
lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex [new file with mode: 0644]
lib/pleroma/web/router.ex
mix.exs
mix.lock
priv/repo/migrations/20210818023112_add_user_id_to_apps.exs [new file with mode: 0644]
priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs [new file with mode: 0644]
test/pleroma/gun/connection_pool_test.exs
test/pleroma/web/mastodon_api/controllers/account_controller_test.exs
test/pleroma/web/mastodon_api/controllers/app_controller_test.exs
test/pleroma/web/o_auth/app_test.exs
test/pleroma/web/pleroma_api/controllers/app_controller_test.exs [new file with mode: 0644]
test/pleroma/web/pleroma_api/views/app_view_test.exs [new file with mode: 0644]
test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs [new file with mode: 0644]

index ee9e0456854b4165975101152bc557a7a0a7b16e..79b669782d0e7089e672eea4bd76c57e01deacbf 100644 (file)
@@ -15,11 +15,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
 ### Added
 - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
+- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
+- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
+- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
+- PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction`
+- MastoAPI: Add `GET /api/v1/accounts/lookup`
+- MastoAPI: Profile Directory support
+- MastoAPI: Support v2 Suggestions (handpicked accounts only)
+- Ability to log slow Ecto queries by configuring `:pleroma, :telemetry, :slow_queries_logging`
+- Added Phoenix LiveDashboard at `/phoenix/live_dashboard`
+- Added `/manifest.json` for progressive web apps.
 
 ### Fixed
 - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
 - Handle Reject for already-accepted Follows properly
 - Display OpenGraph data on alternative notice routes.
+- Fix replies count for remote replies
+- ChatAPI: Add link headers
+- Limited number of search results to 40 to prevent DoS attacks
+- ActivityPub: fixed federation of attachment dimensions
+- Fixed benchmarks
+- Elixir 1.13 support
+- Fixed crash when pinned_objects is nil
+- Fixed slow timelines when there are a lot of deactivated users
+- Fixed account deletion API
 
 ### Removed
 
@@ -67,7 +86,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 - Attachment dimensions and blurhashes are federated when available.
 - Mastodon API: support `poll` notification.
 - Pinned posts federation
-- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
 
 ### Fixed
 - Don't crash so hard when email settings are invalid.
index 23c41edddaf789cd1fe51d43733b1be654154578..2bde5b8268ed61e6cf988daee6faba2ff0973ffd 100644 (file)
@@ -149,6 +149,8 @@ config :pleroma, Pleroma.Web.Endpoint,
   ]
 
 # Configures Elixir's Logger
+config :logger, truncate: 65536
+
 config :logger, :console,
   level: :debug,
   format: "\n$time $metadata[$level] $message\n",
@@ -255,7 +257,8 @@ config :pleroma, :instance,
   ],
   show_reactions: true,
   password_reset_token_validity: 60 * 60 * 24,
-  profile_directory: true
+  profile_directory: true,
+  privileged_staff: false
 
 config :pleroma, :welcome,
   direct_message: [
@@ -853,6 +856,13 @@ config :pleroma, ConcurrentLimiter, [
   {Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]}
 ]
 
+config :pleroma, :telemetry,
+  slow_queries_logging: [
+    enabled: false,
+    min_duration: 500_000,
+    exclude_sources: [nil, "oban_jobs"]
+  ]
+
 # 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"
index 517077acf58be36d757a5a2b262932df3e44e06b..ea3f34abe6c3a71df1d6a121d16ec799ff9d5780 100644 (file)
@@ -941,6 +941,12 @@ config :pleroma, :config_description, [
         key: :profile_directory,
         type: :boolean,
         description: "Enable profile directory."
+      },
+      %{
+        key: :privileged_staff,
+        type: :boolean,
+        description:
+          "Let moderators access sensitive data (e.g. updating user credentials, get password reset token, delete users, index and read private statuses and chats)"
       }
     ]
   },
index 9824e0a4ac3353ff195014cdcb591f6d1b809dda..952579c7f87dab951739930a05b9e1eff32ec8e5 100644 (file)
@@ -61,6 +61,11 @@ defmodule Pleroma.Application do
 
     adapter = Application.get_env(:tesla, :adapter)
 
+    if match?({Tesla.Adapter.Finch, _}, adapter) do
+      Logger.info("Starting Finch")
+      Finch.start_link(name: MyFinch)
+    end
+
     if adapter == Tesla.Adapter.Gun do
       if version = Pleroma.OTPVersion.version() do
         [major, minor] =
index 06dd29fea037ae36760c7c7ffe5bac7c84baeb2d..ce144559f6dc70945b55dbcadb14843d2d71dfb4 100644 (file)
@@ -25,5 +25,6 @@ defmodule Pleroma.ReverseProxy.Client.Wrapper do
 
   defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
   defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
+  defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney
   defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
 end
index 10165c1b221f9a84f8c6690e756da5fef13d2f6b..35e245237ec5abf5004b890df5e273eddd1ac247 100644 (file)
@@ -12,10 +12,16 @@ defmodule Pleroma.Telemetry.Logger do
     [:pleroma, :connection_pool, :reclaim, :stop],
     [:pleroma, :connection_pool, :provision_failure],
     [:pleroma, :connection_pool, :client, :dead],
-    [:pleroma, :connection_pool, :client, :add]
+    [:pleroma, :connection_pool, :client, :add],
+    [:pleroma, :repo, :query]
   ]
   def attach do
-    :telemetry.attach_many("pleroma-logger", @events, &handle_event/4, [])
+    :telemetry.attach_many(
+      "pleroma-logger",
+      @events,
+      &Pleroma.Telemetry.Logger.handle_event/4,
+      []
+    )
   end
 
   # Passing anonymous functions instead of strings to logger is intentional,
@@ -87,4 +93,64 @@ defmodule Pleroma.Telemetry.Logger do
   end
 
   def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
+
+  def handle_event(
+        [:pleroma, :repo, :query] = _name,
+        %{query_time: query_time} = measurements,
+        %{source: source} = metadata,
+        config
+      ) do
+    logging_config = Pleroma.Config.get([:telemetry, :slow_queries_logging], [])
+
+    if logging_config[:enabled] &&
+         logging_config[:min_duration] &&
+         query_time > logging_config[:min_duration] and
+         (is_nil(logging_config[:exclude_sources]) or
+            source not in logging_config[:exclude_sources]) do
+      log_slow_query(measurements, metadata, config)
+    else
+      :ok
+    end
+  end
+
+  defp log_slow_query(
+         %{query_time: query_time} = _measurements,
+         %{source: _source, query: query, params: query_params, repo: repo} = _metadata,
+         _config
+       ) do
+    sql_explain =
+      with {:ok, %{rows: explain_result_rows}} <-
+             repo.query("EXPLAIN " <> query, query_params, log: false) do
+        Enum.map_join(explain_result_rows, "\n", & &1)
+      end
+
+    {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)
+
+    pleroma_stacktrace =
+      Enum.filter(stacktrace, fn
+        {__MODULE__, _, _, _} ->
+          false
+
+        {mod, _, _, _} ->
+          mod
+          |> to_string()
+          |> String.starts_with?("Elixir.Pleroma.")
+      end)
+
+    Logger.warn(fn ->
+      """
+      Slow query!
+
+      Total time: #{round(query_time / 1_000)} ms
+
+      #{query}
+
+      #{inspect(query_params, limit: :infinity)}
+
+      #{sql_explain}
+
+      #{Exception.format_stacktrace(pleroma_stacktrace)}
+      """
+    end)
+  end
 end
index 8cd2e824d1e47d07eb5c1c24cefc7a869c043cc3..f5304d7d6fd4d79cdcadbc51b0df680618daad46 100644 (file)
@@ -400,6 +400,26 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
     }
   end
 
+  def lookup_operation do
+    %Operation{
+      tags: ["Account lookup"],
+      summary: "Find a user by nickname",
+      operationId: "AccountController.lookup",
+      parameters: [
+        Operation.parameter(
+          :acct,
+          :query,
+          :string,
+          "User nickname"
+        )
+      ],
+      responses: %{
+        200 => Operation.response("Account", "application/json", Account),
+        404 => Operation.response("Error", "application/json", ApiError)
+      }
+    }
+  end
+
   def endorsements_operation do
     %Operation{
       tags: ["Retrieve account information"],
index dfb1c71706c4c4ed6b6be97f986f1c6af97aa81d..2284ac127658778ec9fce13d1d35b0b28f761da3 100644 (file)
@@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
   alias OpenApiSpex.Operation
   alias OpenApiSpex.Schema
   alias Pleroma.Web.ApiSpec.Helpers
+  alias Pleroma.Web.ApiSpec.Schemas.App
 
   @spec open_api_operation(atom) :: Operation.t()
   def open_api_operation(action) do
@@ -22,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
       operationId: "AppController.create",
       requestBody: Helpers.request_body("Parameters", create_request(), required: true),
       responses: %{
-        200 => Operation.response("App", "application/json", create_response()),
+        200 => Operation.response("App", "application/json", App),
         422 =>
           Operation.response(
             "Unprocessable Entity",
@@ -119,30 +120,4 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
       }
     }
   end
-
-  defp create_response do
-    %Schema{
-      title: "AppCreateResponse",
-      description: "Response schema for an app",
-      type: :object,
-      properties: %{
-        id: %Schema{type: :string},
-        name: %Schema{type: :string},
-        client_id: %Schema{type: :string},
-        client_secret: %Schema{type: :string},
-        redirect_uri: %Schema{type: :string},
-        vapid_key: %Schema{type: :string},
-        website: %Schema{type: :string, nullable: true}
-      },
-      example: %{
-        "id" => "123",
-        "name" => "My App",
-        "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
-        "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
-        "vapid_key" =>
-          "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
-        "website" => "https://myapp.com/"
-      }
-    }
-  end
 end
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
new file mode 100644 (file)
index 0000000..582a169
--- /dev/null
@@ -0,0 +1,31 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do
+  alias OpenApiSpex.Operation
+  alias OpenApiSpex.Schema
+  alias Pleroma.Web.ApiSpec.Schemas.App
+
+  def open_api_operation(action) do
+    operation = String.to_existing_atom("#{action}_operation")
+    apply(__MODULE__, operation, [])
+  end
+
+  @spec index_operation() :: Operation.t()
+  def index_operation do
+    %Operation{
+      tags: ["Applications"],
+      summary: "List applications",
+      description: "List the OAuth applications for the current user",
+      operationId: "AppController.index",
+      responses: %{
+        200 => Operation.response("Array of App", "application/json", array_of_apps())
+      }
+    }
+  end
+
+  defp array_of_apps do
+    %Schema{type: :array, items: App, example: [App.schema().example]}
+  end
+end
diff --git a/lib/pleroma/web/api_spec/schemas/app.ex b/lib/pleroma/web/api_spec/schemas/app.ex
new file mode 100644 (file)
index 0000000..c3d1af3
--- /dev/null
@@ -0,0 +1,33 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.Schemas.App do
+  alias OpenApiSpex.Schema
+
+  require OpenApiSpex
+
+  OpenApiSpex.schema(%{
+    title: "App",
+    description: "Response schema for an app",
+    type: :object,
+    properties: %{
+      id: %Schema{type: :string},
+      name: %Schema{type: :string},
+      client_id: %Schema{type: :string},
+      client_secret: %Schema{type: :string},
+      redirect_uri: %Schema{type: :string},
+      vapid_key: %Schema{type: :string},
+      website: %Schema{type: :string, nullable: true}
+    },
+    example: %{
+      "id" => "123",
+      "name" => "My App",
+      "client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
+      "client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
+      "vapid_key" =>
+        "BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
+      "website" => "https://myapp.com/"
+    }
+  })
+end
index 5dfbecf5a3835b51b9d2eb254fd6456c377a7d62..a307807a93148c1f8199ad314de9d8e3230a3c67 100644 (file)
@@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
 
   plug(Pleroma.Web.ApiSpec.CastAndValidate)
 
-  plug(:skip_auth when action == :create)
+  plug(:skip_auth when action in [:create, :lookup])
 
   plug(:skip_public_check when action in [:show, :statuses])
 
@@ -492,6 +492,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
     |> render("index.json", users: users, for: user, as: :user)
   end
 
+  @doc "GET /api/v1/accounts/lookup"
+  def lookup(conn, %{acct: nickname} = _params) do
+    with %User{} = user <- User.get_by_nickname(nickname) do
+      render(conn, "show.json",
+        user: user,
+        skip_visibility_check: true
+      )
+    else
+      error -> user_visibility_error(conn, error)
+    end
+  end
+
   @doc "GET /api/v1/endorsements"
   def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
 
index 93e63ba03826fdaa2f33a0608c95aa61b61b5aea..8d18140add66b39d3881ce081cb5ed5dfaae843b 100644 (file)
@@ -10,7 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
 
   use Pleroma.Web, :controller
 
+  alias Pleroma.Maps
   alias Pleroma.Repo
+  alias Pleroma.User
   alias Pleroma.Web.OAuth.App
   alias Pleroma.Web.OAuth.Scopes
   alias Pleroma.Web.OAuth.Token
@@ -26,11 +28,13 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
   @doc "POST /api/v1/apps"
   def create(%{body_params: params} = conn, _params) do
     scopes = Scopes.fetch_scopes(params, ["read"])
+    user_id = get_user_id(conn)
 
     app_attrs =
       params
       |> Map.take([:client_name, :redirect_uris, :website])
       |> Map.put(:scopes, scopes)
+      |> Maps.put_if_present(:user_id, user_id)
 
     with cs <- App.register_changeset(%App{}, app_attrs),
          {:ok, app} <- Repo.insert(cs) do
@@ -38,6 +42,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
     end
   end
 
+  defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
+  defp get_user_id(_conn), do: nil
+
   @doc """
   GET /api/v1/apps/verify_credentials
   Gets compact non-secret representation of the app. Supports app tokens and user tokens.
index 7072d5d61b1489a88e705496ef01a3f31afe823f..8e657ee0f379acddeacbfc69ba2c2031282e778d 100644 (file)
@@ -45,7 +45,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
           features: features(),
           federation: federation(),
           fields_limits: fields_limits(),
-          post_formats: Config.get([:instance, :allowed_post_formats])
+          post_formats: Config.get([:instance, :allowed_post_formats]),
+          privileged_staff: Config.get([:instance, :privileged_staff])
         },
         stats: %{mau: Pleroma.User.active_user_count()},
         vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
index 3781781c849ae3ad8618bd0c8ddc00edd2fb5f8d..80a2ce67681c4f9b07de3b79c9430de0f9eee75f 100644 (file)
@@ -69,7 +69,8 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
         mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),
         features: features,
         restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
-        skipThreadContainment: Config.get([:instance, :skip_thread_containment], false)
+        skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
+        privilegedStaff: Config.get([:instance, :privileged_staff])
       }
     }
   end
index 38275001036177a142cb0f2d436242bd2cc9063a..dacfbadc8ed174c881e5664a8b7de58f2bb89077 100644 (file)
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
   import Ecto.Changeset
   import Ecto.Query
   alias Pleroma.Repo
+  alias Pleroma.User
 
   @type t :: %__MODULE__{}
 
@@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do
     field(:client_secret, :string)
     field(:trusted, :boolean, default: false)
 
+    belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
+
     has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
     has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
 
@@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do
 
   @spec changeset(t(), map()) :: Ecto.Changeset.t()
   def changeset(struct, params) do
-    cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
+    cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
   end
 
   @spec register_changeset(t(), map()) :: Ecto.Changeset.t()
@@ -129,6 +132,12 @@ defmodule Pleroma.Web.OAuth.App do
     {:ok, Repo.all(query), count}
   end
 
+  @spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()}
+  def get_user_apps(%User{id: user_id}) do
+    from(a in __MODULE__, where: a.user_id == ^user_id)
+    |> Repo.all()
+  end
+
   @spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
   def destroy(id) do
     with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
diff --git a/lib/pleroma/web/pleroma_api/controllers/app_controller.ex b/lib/pleroma/web/pleroma_api/controllers/app_controller.ex
new file mode 100644 (file)
index 0000000..d857f42
--- /dev/null
@@ -0,0 +1,23 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppController do
+  use Pleroma.Web, :controller
+
+  alias Pleroma.Web.OAuth.App
+  alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+  plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index])
+
+  plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+  defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation
+
+  @doc "GET /api/v1/pleroma/apps"
+  def index(%{assigns: %{user: user}} = conn, _params) do
+    with apps <- App.get_user_apps(user) do
+      render(conn, "index.json", %{apps: apps})
+    end
+  end
+end
diff --git a/lib/pleroma/web/pleroma_api/views/app_view.ex b/lib/pleroma/web/pleroma_api/views/app_view.ex
new file mode 100644 (file)
index 0000000..6b5d838
--- /dev/null
@@ -0,0 +1,11 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppView do
+  use Pleroma.Web, :view
+
+  def render("index.json", %{apps: apps}) do
+    render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json")
+  end
+end
diff --git a/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex b/lib/pleroma/web/plugs/ensure_staff_privileged_plug.ex
new file mode 100644 (file)
index 0000000..c6ed456
--- /dev/null
@@ -0,0 +1,36 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug do
+  @moduledoc """
+  Ensures staff are privileged enough to do certain tasks.
+  """
+  import Pleroma.Web.TranslationHelpers
+  import Plug.Conn
+
+  alias Pleroma.Config
+  alias Pleroma.User
+
+  def init(options) do
+    options
+  end
+
+  def call(%{assigns: %{user: %User{is_admin: true}}} = conn, _), do: conn
+
+  def call(%{assigns: %{user: %User{is_moderator: true}}} = conn, _) do
+    if Config.get!([:instance, :privileged_staff]) do
+      conn
+    else
+      conn
+      |> render_error(:forbidden, "User is not an admin.")
+      |> halt()
+    end
+  end
+
+  def call(conn, _) do
+    conn
+    |> render_error(:forbidden, "User is not a staff member.")
+    |> halt()
+  end
+end
index b2ca0978436ce3a8a2bf14b6dcef73f8e0a21ca0..b9b52b1e54d75577f3008f84b86ebba81fc0a53e 100644 (file)
@@ -101,6 +101,10 @@ defmodule Pleroma.Web.Router do
     plug(Pleroma.Web.Plugs.IdempotencyPlug)
   end
 
+  pipeline :require_privileged_staff do
+    plug(Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug)
+  end
+
   pipeline :require_admin do
     plug(Pleroma.Web.Plugs.UserIsAdminPlug)
   end
@@ -195,7 +199,6 @@ defmodule Pleroma.Web.Router do
     post("/relay", RelayController, :follow)
     delete("/relay", RelayController, :unfollow)
 
-    get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
     patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
     get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
     patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
@@ -228,6 +231,24 @@ defmodule Pleroma.Web.Router do
     post("/backups", AdminAPIController, :create_backup)
   end
 
+  # AdminAPI: admins and mods (staff) can perform these actions (if enabled by config)
+  scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
+    pipe_through([:admin_api, :require_privileged_staff])
+
+    delete("/users", UserController, :delete)
+
+    get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
+    patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
+
+    get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
+    get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
+
+    get("/statuses", StatusController, :index)
+
+    get("/chats/:id", ChatController, :show)
+    get("/chats/:id/messages", ChatController, :messages)
+  end
+
   # AdminAPI: admins and mods (staff) can perform these actions
   scope "/api/v1/pleroma/admin", Pleroma.Web.AdminAPI do
     pipe_through(:admin_api)
@@ -240,22 +261,13 @@ defmodule Pleroma.Web.Router do
     patch("/users/deactivate", UserController, :deactivate)
     patch("/users/approve", UserController, :approve)
 
-    delete("/users", UserController, :delete)
-
     post("/users/invite_token", InviteController, :create)
     get("/users/invites", InviteController, :index)
     post("/users/revoke_invite", InviteController, :revoke)
     post("/users/email_invite", InviteController, :email)
 
-    get("/users/:nickname/password_reset", AdminAPIController, :get_password_reset)
-    patch("/users/force_password_reset", AdminAPIController, :force_password_reset)
-    get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
-    patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
-
     get("/users", UserController, :index)
     get("/users/:nickname", UserController, :show)
-    get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
-    get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
 
     get("/instances/:instance/statuses", InstanceController, :list_statuses)
     delete("/instances/:instance", InstanceController, :delete)
@@ -269,15 +281,12 @@ defmodule Pleroma.Web.Router do
     get("/statuses/:id", StatusController, :show)
     put("/statuses/:id", StatusController, :update)
     delete("/statuses/:id", StatusController, :delete)
-    get("/statuses", StatusController, :index)
 
     get("/moderation_log", AdminAPIController, :list_log)
 
     post("/reload_emoji", AdminAPIController, :reload_emoji)
     get("/stats", AdminAPIController, :stats)
 
-    get("/chats/:id", ChatController, :show)
-    get("/chats/:id/messages", ChatController, :messages)
     delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
   end
 
@@ -386,6 +395,7 @@ defmodule Pleroma.Web.Router do
   scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
     pipe_through(:api)
 
+    get("/apps", AppController, :index)
     get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
     get("/statuses/:id/reactions", EmojiReactionController, :index)
   end
@@ -575,6 +585,8 @@ defmodule Pleroma.Web.Router do
     get("/accounts/search", SearchController, :account_search)
     get("/search", SearchController, :search)
 
+    get("/accounts/lookup", AccountController, :lookup)
+
     get("/accounts/:id/statuses", AccountController, :statuses)
     get("/accounts/:id/followers", AccountController, :followers)
     get("/accounts/:id/following", AccountController, :following)
diff --git a/mix.exs b/mix.exs
index 615f1d50b87ae4f8876b9986b7791347cf844ae0..360d499050ab28766a1ae676079c400b6b44486d 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -137,6 +137,7 @@ defmodule Pleroma.Mixfile do
       {:castore, "~> 0.1"},
       {:cowlib, "~> 2.9", override: true},
       {:gun, "~> 2.0.0-rc.1", override: true},
+      {:finch, "~> 0.10.0"},
       {:jason, "~> 1.2"},
       {:mogrify, "~> 0.9.1"},
       {:ex_aws, "~> 2.1.6"},
@@ -192,9 +193,7 @@ defmodule Pleroma.Mixfile do
        git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
        ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
       {:restarter, path: "./restarter"},
-      {:majic,
-       git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git",
-       ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"},
+      {:majic, "~> 1.0"},
       {:eblurhash, "~> 1.1.0"},
       {:open_api_spex, "~> 3.10"},
       {:phoenix_live_dashboard, "~> 0.6.2"},
index 5b95272046634fe8ff4e36d2ca21dd6caead05c4..f371a6e41d6ed80b5a8184f2ed4da8b7a5c5a1c4 100644 (file)
--- a/mix.lock
+++ b/mix.lock
   "ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
   "ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
   "excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
-  "fast_html": {:hex, :fast_html, "2.0.4", "4910ee49f2f6b19692e3bf30bf97f1b6b7dac489cd6b0f34cd0fe3042c56ba30", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "3bb49d541dfc02ad5e425904f53376d758c09f89e521afc7d2b174b3227761ea"},
+  "fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
   "fast_sanitize": {:hex, :fast_sanitize, "0.2.2", "3cbbaebaea6043865dfb5b4ecb0f1af066ad410a51470e353714b10c42007b81", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "69f204db9250afa94a0d559d9110139850f57de2b081719fbafa1e9a89e94466"},
   "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
+  "finch": {:hex, :finch, "0.10.0", "8e5e6101ae98e7f1ef830594f774411a2f9cbce4f92d8179502da69fbbff52bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80324ba22edbdebca6fac05c8517e7457b79dfe101e3bf6b2f7c5c65c93a9077"},
   "flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
   "floki": {:hex, :floki, "0.30.1", "75d35526d3a1459920b6e87fdbc2e0b8a3670f965dd0903708d2b267e0904c55", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e9c03524447d1c4cbfccd672d739b8c18453eee377846b119d4fd71b1a176bb8"},
   "gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
@@ -69,7 +70,7 @@
   "jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
   "libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
   "linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"},
-  "majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "289cda1b6d0d70ccb2ba508a2b0bd24638db2880", [ref: "289cda1b6d0d70ccb2ba508a2b0bd24638db2880"]},
+  "majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
   "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
   "makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
   "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
   "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
   "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"},
   "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
+  "mint": {:hex, :mint, "1.4.0", "cd7d2451b201fc8e4a8fd86257fb3878d9e3752899eb67b0c5b25b180bde1212", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "10a99e144b815cbf8522dccbc8199d15802440fc7a64d67b6853adb6fa170217"},
   "mochiweb": {:hex, :mochiweb, "2.18.0", "eb55f1db3e6e960fac4e6db4e2db9ec3602cc9f30b86cd1481d56545c3145d2e", [:rebar3], [], "hexpm"},
   "mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
   "mogrify": {:hex, :mogrify, "0.9.1", "a26f107c4987477769f272bd0f7e3ac4b7b75b11ba597fd001b877beffa9c068", [:mix], [], "hexpm", "134edf189337d2125c0948bf0c228fdeef975c594317452d536224069a5b7f05"},
   "mox": {:hex, :mox, "1.0.0", "4b3c7005173f47ff30641ba044eb0fe67287743eec9bd9545e37f3002b0a9f8b", [:mix], [], "hexpm", "201b0a20b7abdaaab083e9cf97884950f8a30a1350a1da403b3145e213c6f4df"},
   "myhtmlex": {:git, "https://git.pleroma.social/pleroma/myhtmlex.git", "ad0097e2f61d4953bfef20fb6abddf23b87111e6", [ref: "ad0097e2f61d4953bfef20fb6abddf23b87111e6", submodules: true]},
+  "nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
   "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
-  "nimble_pool": {:hex, :nimble_pool, "0.1.0", "ffa9d5be27eee2b00b0c634eb649aa27f97b39186fec3c493716c2a33e784ec6", [:mix], [], "hexpm", "343a1eaa620ddcf3430a83f39f2af499fe2370390d4f785cd475b4df5acaf3f9"},
+  "nimble_pool": {:hex, :nimble_pool, "0.2.4", "1db8e9f8a53d967d595e0b32a17030cdb6c0dc4a451b8ac787bf601d3f7704c3", [:mix], [], "hexpm", "367e8071e137b787764e6a9992ccb57b276dc2282535f767a07d881951ebeac6"},
   "nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
   "oban": {:hex, :oban, "2.3.4", "ec7509b9af2524d55f529cb7aee93d36131ae0bf0f37706f65d2fe707f4d9fd8", [:mix], [{:ecto_sql, ">= 3.4.3", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c70ca0434758fd1805422ea4446af5e910ddc697c0c861549c8f0eb0cfbd2fdf"},
   "open_api_spex": {:hex, :open_api_spex, "3.10.0", "94e9521ad525b3fcf6dc77da7c45f87fdac24756d4de588cb0816b413e7c1844", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "2dbb2bde3d2b821f06936e8dfaf3284331186556291946d84eeba3750ac28765"},
diff --git a/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs b/priv/repo/migrations/20210818023112_add_user_id_to_apps.exs
new file mode 100644 (file)
index 0000000..88a6bce
--- /dev/null
@@ -0,0 +1,11 @@
+defmodule Pleroma.Repo.Migrations.AddUserIdToApps do
+  use Ecto.Migration
+
+  def change do
+    alter table(:apps) do
+      add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
+    end
+
+    create_if_not_exists(index(:apps, [:user_id]))
+  end
+end
diff --git a/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs b/priv/repo/migrations/20211229075801_user_relationships_target_id_relationship_type_index.exs
new file mode 100644 (file)
index 0000000..f3eb840
--- /dev/null
@@ -0,0 +1,7 @@
+defmodule Pleroma.Repo.Migrations.UserRelationshipsTargetIdRelationshipTypeIndex do
+  use Ecto.Migration
+
+  def change do
+    create_if_not_exists(index(:user_relationships, [:target_id, :relationship_type]))
+  end
+end
index 4b315862560a9ec0a0f0b9ada3f382b7b743d29a..51637f5412d95df5398c7966e9e0070b2a705fb0 100644 (file)
@@ -46,6 +46,7 @@ defmodule Pleroma.Gun.ConnectionPoolTest do
       end
   end
 
+  @tag :erratic
   test "connection limit is respected with concurrent requests" do
     clear_config([:connections_pool, :max_connections]) do
       clear_config([:connections_pool, :max_connections], 1)
index 966a4072d4665c92c42bd8a5247a62b49d05bb17..374e2048a732114a60c521bcd1a2073d24201a83 100644 (file)
@@ -1798,6 +1798,30 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
     assert [%{"id" => ^id2}] = result
   end
 
+  test "account lookup", %{conn: conn} do
+    %{nickname: acct} = insert(:user, %{nickname: "nickname"})
+    %{nickname: acct_two} = insert(:user, %{nickname: "nickname@notlocaldoma.in"})
+
+    result =
+      conn
+      |> get("/api/v1/accounts/lookup?acct=#{acct}")
+      |> json_response_and_validate_schema(200)
+
+    assert %{"acct" => ^acct} = result
+
+    result =
+      conn
+      |> get("/api/v1/accounts/lookup?acct=#{acct_two}")
+      |> json_response_and_validate_schema(200)
+
+    assert %{"acct" => ^acct_two} = result
+
+    _result =
+      conn
+      |> get("/api/v1/accounts/lookup?acct=unexisting_nickname")
+      |> json_response_and_validate_schema(404)
+  end
+
   test "create a note on a user" do
     %{conn: conn} = oauth_access(["write:accounts", "read:follows"])
     other_user = insert(:user)
index 76d81b942333274ae69d3c279375fa82c52aff88..bfbb7f32d676a4a2214a887f5c6e2c8eb1cca649 100644 (file)
@@ -35,6 +35,33 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
   end
 
   test "creates an oauth app", %{conn: conn} do
+    app_attrs = build(:oauth_app)
+
+    conn =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> post("/api/v1/apps", %{
+        client_name: app_attrs.client_name,
+        redirect_uris: app_attrs.redirect_uris
+      })
+
+    [app] = Repo.all(App)
+
+    expected = %{
+      "name" => app.client_name,
+      "website" => app.website,
+      "client_id" => app.client_id,
+      "client_secret" => app.client_secret,
+      "id" => app.id |> to_string(),
+      "redirect_uri" => app.redirect_uris,
+      "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
+    }
+
+    assert expected == json_response_and_validate_schema(conn, 200)
+    assert app.user_id == nil
+  end
+
+  test "creates an oauth app with a user", %{conn: conn} do
     user = insert(:user)
     app_attrs = build(:oauth_app)
 
@@ -60,5 +87,6 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
     }
 
     assert expected == json_response_and_validate_schema(conn, 200)
+    assert app.user_id == user.id
   end
 end
index fc2f0d94080812820d1a3102a73f327856ffbbd8..a5223b0a53268d31c1b1213602767c9c0c33afd1 100644 (file)
@@ -41,4 +41,16 @@ defmodule Pleroma.Web.OAuth.AppTest do
       assert error.type == :unique
     end
   end
+
+  test "get_user_apps/1" do
+    user = insert(:user)
+
+    apps = [
+      insert(:oauth_app, user_id: user.id),
+      insert(:oauth_app, user_id: user.id),
+      insert(:oauth_app, user_id: user.id)
+    ]
+
+    assert App.get_user_apps(user) == apps
+  end
 end
diff --git a/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs b/test/pleroma/web/pleroma_api/controllers/app_controller_test.exs
new file mode 100644 (file)
index 0000000..5e24e18
--- /dev/null
@@ -0,0 +1,53 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppControllerTest do
+  use Pleroma.Web.ConnCase, async: true
+
+  alias Pleroma.Web.OAuth.App
+  alias Pleroma.Web.Push
+
+  import Pleroma.Factory
+
+  test "apps", %{conn: conn} do
+    user = insert(:user)
+    app_attrs = build(:oauth_app)
+
+    creation =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> assign(:user, user)
+      |> post("/api/v1/apps", %{
+        client_name: app_attrs.client_name,
+        redirect_uris: app_attrs.redirect_uris
+      })
+
+    [app] = App.get_user_apps(user)
+
+    expected = %{
+      "name" => app.client_name,
+      "website" => app.website,
+      "client_id" => app.client_id,
+      "client_secret" => app.client_secret,
+      "id" => app.id |> to_string(),
+      "redirect_uri" => app.redirect_uris,
+      "vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
+    }
+
+    assert expected == json_response_and_validate_schema(creation, 200)
+
+    response =
+      conn
+      |> put_req_header("content-type", "application/json")
+      |> assign(:user, user)
+      |> assign(:token, insert(:oauth_token, user: user, scopes: ["read", "follow"]))
+      |> get("/api/v1/pleroma/apps")
+      |> json_response_and_validate_schema(200)
+
+    [apps] = response
+
+    assert length(response) == 1
+    assert apps["client_id"] == app.client_id
+  end
+end
diff --git a/test/pleroma/web/pleroma_api/views/app_view_test.exs b/test/pleroma/web/pleroma_api/views/app_view_test.exs
new file mode 100644 (file)
index 0000000..f0aee69
--- /dev/null
@@ -0,0 +1,21 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.AppViewTest do
+  use Pleroma.DataCase, async: true
+  alias Pleroma.Web.PleromaAPI.AppView
+  import Pleroma.Factory
+
+  test "index.json" do
+    apps = [
+      insert(:oauth_app),
+      insert(:oauth_app),
+      insert(:oauth_app)
+    ]
+
+    results = AppView.render("index.json", %{apps: apps})
+
+    assert [%{client_id: _, client_secret: _}, _, _] = results
+  end
+end
diff --git a/test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs b/test/pleroma/web/plugs/ensure_staff_privileged_plug_test.exs
new file mode 100644 (file)
index 0000000..74f4ae5
--- /dev/null
@@ -0,0 +1,60 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.Plugs.EnsureStaffPrivilegedPlugTest do
+  use Pleroma.Web.ConnCase, async: true
+
+  alias Pleroma.Web.Plugs.EnsureStaffPrivilegedPlug
+  import Pleroma.Factory
+
+  test "accepts a user that is an admin" do
+    user = insert(:user, is_admin: true)
+
+    conn = assign(build_conn(), :user, user)
+
+    ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
+
+    assert conn == ret_conn
+  end
+
+  test "accepts a user that is a moderator when :privileged_staff is enabled" do
+    clear_config([:instance, :privileged_staff], true)
+    user = insert(:user, is_moderator: true)
+
+    conn = assign(build_conn(), :user, user)
+
+    ret_conn = EnsureStaffPrivilegedPlug.call(conn, %{})
+
+    assert conn == ret_conn
+  end
+
+  test "denies a user that is a moderator when :privileged_staff is disabled" do
+    clear_config([:instance, :privileged_staff], false)
+    user = insert(:user, is_moderator: true)
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> EnsureStaffPrivilegedPlug.call(%{})
+
+    assert conn.status == 403
+  end
+
+  test "denies a user that isn't a staff member" do
+    user = insert(:user)
+
+    conn =
+      build_conn()
+      |> assign(:user, user)
+      |> EnsureStaffPrivilegedPlug.call(%{})
+
+    assert conn.status == 403
+  end
+
+  test "denies when a user isn't set" do
+    conn = EnsureStaffPrivilegedPlug.call(build_conn(), %{})
+
+    assert conn.status == 403
+  end
+end