Automatic checks of authentication / instance publicity. Definition of missing OAuth scopes in AdminAPIController. Refactoring.
This commit is contained in:
parent
3c828016d9
commit
f685cbd309
|
@ -0,0 +1,33 @@
|
||||||
|
This document contains notes and guidelines for Pleroma developers.
|
||||||
|
|
||||||
|
# Authentication & Authorization
|
||||||
|
|
||||||
|
## OAuth token-based authentication & authorization
|
||||||
|
|
||||||
|
* Pleroma supports hierarchical OAuth scopes, just like Mastodon but with added granularity of admin scopes.
|
||||||
|
For a reference, see [Mastodon OAuth scopes](https://docs.joinmastodon.org/api/oauth-scopes/).
|
||||||
|
|
||||||
|
* It is important to either define OAuth scope restrictions or explicitly mark OAuth scope check as skipped, for every
|
||||||
|
controller action. To define scopes, call `plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: [...]})`. To explicitly set
|
||||||
|
OAuth scopes check skipped, call `plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug <when ...>)`.
|
||||||
|
|
||||||
|
* In controllers, `use Pleroma.Web, :controller` will result in `action/2` (see `Pleroma.Web.controller/0` for definition)
|
||||||
|
be called prior to actual controller action, and it'll perform security / privacy checks before passing control to
|
||||||
|
actual controller action. For routes with `:authenticated_api` pipeline, authentication & authorization are expected,
|
||||||
|
thus `OAuthScopesPlug` will be run unless explicitly skipped (also `EnsureAuthenticatedPlug` will be executed
|
||||||
|
immediately before action even if there was an early run to give an early error, since `OAuthScopesPlug` supports
|
||||||
|
`:proceed_unauthenticated` option, and other plugs may support similar options as well). For `:api` pipeline routes,
|
||||||
|
`EnsurePublicOrAuthenticatedPlug` will be called to ensure that the instance is not private or user is authenticated
|
||||||
|
(unless explicitly skipped). Such automated checks help to prevent human errors and result in higher security / privacy
|
||||||
|
for users.
|
||||||
|
|
||||||
|
## [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization)
|
||||||
|
|
||||||
|
* With HTTP Basic Auth, OAuth scopes check is _not_ performed for any action (since password is provided during the auth,
|
||||||
|
requester is able to obtain a token with full permissions anyways). `Pleroma.Plugs.AuthenticationPlug` and
|
||||||
|
`Pleroma.Plugs.LegacyAuthenticationPlug` both call `Pleroma.Plugs.OAuthScopesPlug.skip_plug(conn)` when password
|
||||||
|
is provided.
|
||||||
|
|
||||||
|
## Auth-related configuration, OAuth consumer mode etc.
|
||||||
|
|
||||||
|
See `Authentication` section of [`docs/configuration/cheatsheet.md`](docs/configuration/cheatsheet.md#authentication).
|
|
@ -1,17 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.AuthExpectedPlug do
|
|
||||||
import Plug.Conn
|
|
||||||
|
|
||||||
def init(options), do: options
|
|
||||||
|
|
||||||
def call(conn, _) do
|
|
||||||
put_private(conn, :auth_expected, true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def auth_expected?(conn) do
|
|
||||||
conn.private[:auth_expected]
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -5,17 +5,21 @@
|
||||||
defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
|
defmodule Pleroma.Plugs.EnsureAuthenticatedPlug do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(%{assigns: %{user: %User{}}} = conn, _) do
|
@impl true
|
||||||
|
def perform(%{assigns: %{user: %User{}}} = conn, _) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, options) do
|
def perform(conn, options) do
|
||||||
perform =
|
perform =
|
||||||
cond do
|
cond do
|
||||||
options[:if_func] -> options[:if_func].()
|
options[:if_func] -> options[:if_func].()
|
||||||
|
|
|
@ -5,14 +5,18 @@
|
||||||
defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
|
defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
def init(options) do
|
def init(options) do
|
||||||
options
|
options
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, _) do
|
@impl true
|
||||||
|
def perform(conn, _) do
|
||||||
public? = Config.get!([:instance, :public])
|
public? = Config.get!([:instance, :public])
|
||||||
|
|
||||||
case {public?, conn} do
|
case {public?, conn} do
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Plugs.ExpectAuthenticatedCheckPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Marks `Pleroma.Plugs.EnsureAuthenticatedPlug` as expected to be executed later in plug chain.
|
||||||
|
|
||||||
|
No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
|
def init(options), do: options
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def perform(conn, _) do
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug do
|
||||||
|
@moduledoc """
|
||||||
|
Marks `Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug` as expected to be executed later in plug
|
||||||
|
chain.
|
||||||
|
|
||||||
|
No-op plug which affects `Pleroma.Web` operation (is checked with `PlugHelper.plug_called?/2`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
|
def init(options), do: options
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def perform(conn, _) do
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,15 +7,12 @@ defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
|
||||||
alias Pleroma.Plugs.PlugHelper
|
|
||||||
|
|
||||||
use Pleroma.Web, :plug
|
use Pleroma.Web, :plug
|
||||||
|
|
||||||
@behaviour Plug
|
|
||||||
|
|
||||||
def init(%{scopes: _} = options), do: options
|
def init(%{scopes: _} = options), do: options
|
||||||
|
|
||||||
|
@impl true
|
||||||
def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
op = options[:op] || :|
|
op = options[:op] || :|
|
||||||
token = assigns[:token]
|
token = assigns[:token]
|
||||||
|
@ -34,7 +31,6 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
conn
|
conn
|
||||||
|> assign(:user, nil)
|
|> assign(:user, nil)
|
||||||
|> assign(:token, nil)
|
|> assign(:token, nil)
|
||||||
|> maybe_perform_instance_privacy_check(options)
|
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
missing_scopes = scopes -- matched_scopes
|
missing_scopes = scopes -- matched_scopes
|
||||||
|
@ -71,12 +67,4 @@ def transform_scopes(scopes, options) do
|
||||||
scopes
|
scopes
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_perform_instance_privacy_check(%Plug.Conn{} = conn, options) do
|
|
||||||
if options[:skip_instance_privacy_check] do
|
|
||||||
conn
|
|
||||||
else
|
|
||||||
EnsurePublicOrAuthenticatedPlug.call(conn, [])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,6 +48,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
%{scopes: ["write:accounts"], admin: true}
|
%{scopes: ["write:accounts"], admin: true}
|
||||||
when action in [
|
when action in [
|
||||||
:get_password_reset,
|
:get_password_reset,
|
||||||
|
:force_password_reset,
|
||||||
:user_delete,
|
:user_delete,
|
||||||
:users_create,
|
:users_create,
|
||||||
:user_toggle_activation,
|
:user_toggle_activation,
|
||||||
|
@ -56,7 +57,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
:tag_users,
|
:tag_users,
|
||||||
:untag_users,
|
:untag_users,
|
||||||
:right_add,
|
:right_add,
|
||||||
|
:right_add_multiple,
|
||||||
:right_delete,
|
:right_delete,
|
||||||
|
:right_delete_multiple,
|
||||||
:update_user_credentials
|
:update_user_credentials
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -84,13 +87,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:reports"], admin: true}
|
%{scopes: ["write:reports"], admin: true}
|
||||||
when action in [:reports_update]
|
when action in [:reports_update, :report_notes_create, :report_notes_delete]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"], admin: true}
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
when action == :list_user_statuses
|
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -102,13 +105,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], admin: true}
|
%{scopes: ["read"], admin: true}
|
||||||
when action in [:config_show, :list_log, :stats]
|
when action in [
|
||||||
|
:config_show,
|
||||||
|
:list_log,
|
||||||
|
:stats,
|
||||||
|
:relay_list,
|
||||||
|
:config_descriptions,
|
||||||
|
:need_reboot
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write"], admin: true}
|
%{scopes: ["write"], admin: true}
|
||||||
when action == :config_update
|
when action in [
|
||||||
|
:restart,
|
||||||
|
:config_update,
|
||||||
|
:resend_confirmation_email,
|
||||||
|
:confirm_email,
|
||||||
|
:oauth_app_create,
|
||||||
|
:oauth_app_list,
|
||||||
|
:oauth_app_update,
|
||||||
|
:oauth_app_delete,
|
||||||
|
:reload_emoji
|
||||||
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
@ -1103,25 +1123,25 @@ def stats(conn, _) do
|
||||||
|> json(%{"status_visibility" => count})
|
|> json(%{"status_visibility" => count})
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
defp errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|> json(dgettext("errors", "Not found"))
|
|> json(dgettext("errors", "Not found"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, reason}) do
|
defp errors(conn, {:error, reason}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(reason)
|
|> json(reason)
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, {:param_cast, _}) do
|
defp errors(conn, {:param_cast, _}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(dgettext("errors", "Invalid parameters"))
|
|> json(dgettext("errors", "Invalid parameters"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, _) do
|
defp errors(conn, _) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:internal_server_error)
|
|> put_status(:internal_server_error)
|
||||||
|> json(dgettext("errors", "Something went wrong"))
|
|> json(dgettext("errors", "Something went wrong"))
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
defmodule Fallback.RedirectController do
|
defmodule Fallback.RedirectController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Metadata
|
alias Pleroma.Web.Metadata
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,14 @@ defmodule Pleroma.Web.MastoFEController do
|
||||||
# Note: :index action handles attempt of unauthenticated access to private instance with redirect
|
# Note: :index action handles attempt of unauthenticated access to private instance with redirect
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true}
|
%{scopes: ["read"], fallback: :proceed_unauthenticated}
|
||||||
when action == :index
|
when action == :index
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :manifest])
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :manifest]
|
||||||
|
)
|
||||||
|
|
||||||
@doc "GET /web/*path"
|
@doc "GET /web/*path"
|
||||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||||
|
|
|
@ -26,12 +26,24 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
|
plug(:skip_plug, OAuthScopesPlug when action in [:create, :identity_proofs])
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
|
when action in [:create, :show, :statuses]
|
||||||
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
||||||
when action == :show
|
when action in [:show, :endorsements]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]}
|
||||||
|
when action == :statuses
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -56,21 +68,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships)
|
plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships)
|
||||||
|
|
||||||
# Note: :follows (POST /api/v1/follows) is the same as :follow, consider removing :follows
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["follow", "write:follows"]} when action in [:follows, :follow, :unfollow]
|
%{scopes: ["follow", "write:follows"]} when action in [:follow_by_uri, :follow, :unfollow]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
|
plug(OAuthScopesPlug, %{scopes: ["follow", "read:mutes"]} when action == :mutes)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||||
|
|
||||||
plug(
|
|
||||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
|
||||||
when action not in [:create, :show, :statuses]
|
|
||||||
)
|
|
||||||
|
|
||||||
@relationship_actions [:follow, :unfollow]
|
@relationship_actions [:follow, :unfollow]
|
||||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
|
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
|
||||||
|
|
||||||
|
@ -356,7 +362,7 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/follows"
|
@doc "POST /api/v1/follows"
|
||||||
def follows(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
def follow_by_uri(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
|
||||||
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
with {_, %User{} = followed} <- {:followed, User.get_cached_by_nickname(uri)},
|
||||||
{_, true} <- {:followed, follower.id != followed.id},
|
{_, true} <- {:followed, follower.id != followed.id},
|
||||||
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
{:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
|
||||||
|
|
|
@ -13,10 +13,10 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
|
||||||
|
|
||||||
plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
|
plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
|
||||||
|
|
||||||
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
|
||||||
@doc "GET /web/login"
|
@doc "GET /web/login"
|
||||||
def login(%{assigns: %{user: %User{}}} = conn, _params) do
|
def login(%{assigns: %{user: %User{}}} = conn, _params) do
|
||||||
redirect(conn, to: local_mastodon_root_path(conn))
|
redirect(conn, to: local_mastodon_root_path(conn))
|
||||||
|
|
|
@ -14,9 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index)
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :read)
|
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action != :index)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/conversations"
|
@doc "GET /api/v1/conversations"
|
||||||
def index(%{assigns: %{user: user}} = conn, params) do
|
def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
@ -28,7 +26,7 @@ def index(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/conversations/:id/read"
|
@doc "POST /api/v1/conversations/:id/read"
|
||||||
def read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
def mark_as_read(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
|
||||||
with %Participation{} = participation <-
|
with %Participation{} = participation <-
|
||||||
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
||||||
{:ok, participation} <- Participation.mark_as_read(participation) do
|
{:ok, participation} <- Participation.mark_as_read(participation) do
|
||||||
|
|
|
@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
|
||||||
%{scopes: ["follow", "write:blocks"]} when action != :index
|
%{scopes: ["follow", "write:blocks"]} when action != :index
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/domain_blocks"
|
@doc "GET /api/v1/domain_blocks"
|
||||||
def index(%{assigns: %{user: user}} = conn, _) do
|
def index(%{assigns: %{user: user}} = conn, _) do
|
||||||
json(conn, Map.get(user, :domain_blocks, []))
|
json(conn, Map.get(user, :domain_blocks, []))
|
||||||
|
|
|
@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.FilterController do
|
||||||
%{scopes: ["write:filters"]} when action not in @oauth_read_actions
|
%{scopes: ["write:filters"]} when action not in @oauth_read_actions
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/filters"
|
@doc "GET /api/v1/filters"
|
||||||
def index(%{assigns: %{user: user}} = conn, _) do
|
def index(%{assigns: %{user: user}} = conn, _) do
|
||||||
filters = Filter.get_filters(user)
|
filters = Filter.get_filters(user)
|
||||||
|
|
|
@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
||||||
%{scopes: ["follow", "write:follows"]} when action != :index
|
%{scopes: ["follow", "write:follows"]} when action != :index
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/follow_requests"
|
@doc "GET /api/v1/follow_requests"
|
||||||
def index(%{assigns: %{user: followed}} = conn, _params) do
|
def index(%{assigns: %{user: followed}} = conn, _params) do
|
||||||
follow_requests = User.get_follow_requests(followed)
|
follow_requests = User.get_follow_requests(followed)
|
||||||
|
|
|
@ -11,16 +11,16 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
||||||
|
|
||||||
plug(:list_by_id_and_user when action not in [:index, :create])
|
plug(:list_by_id_and_user when action not in [:index, :create])
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in [:index, :show, :list_accounts])
|
@oauth_read_actions [:index, :show, :list_accounts]
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action in @oauth_read_actions)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:lists"]}
|
%{scopes: ["write:lists"]}
|
||||||
when action in [:create, :update, :delete, :add_to_list, :remove_from_list]
|
when action not in @oauth_read_actions
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
# GET /api/v1/lists
|
# GET /api/v1/lists
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
|
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
# GET /api/v1/markers
|
# GET /api/v1/markers
|
||||||
|
|
|
@ -17,8 +17,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|
|
||||||
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
|
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
def empty_array(conn, _) do
|
def empty_array(conn, _) do
|
||||||
|
|
|
@ -15,8 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:media"]})
|
plug(OAuthScopesPlug, %{scopes: ["write:media"]})
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "POST /api/v1/media"
|
@doc "POST /api/v1/media"
|
||||||
def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
|
|
|
@ -20,8 +20,6 @@ defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action not in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action not in @oauth_read_actions)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
# GET /api/v1/notifications
|
# GET /api/v1/notifications
|
||||||
def index(conn, %{"account_id" => account_id} = params) do
|
def index(conn, %{"account_id" => account_id} = params) do
|
||||||
case Pleroma.User.get_cached_by_id(account_id) do
|
case Pleroma.User.get_cached_by_id(account_id) do
|
||||||
|
|
|
@ -22,8 +22,6 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote)
|
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :vote)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/polls/:id"
|
@doc "GET /api/v1/polls/:id"
|
||||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||||
|
|
|
@ -11,8 +11,6 @@ defmodule Pleroma.Web.MastodonAPI.ReportController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create)
|
plug(OAuthScopesPlug, %{scopes: ["write:reports"]} when action == :create)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "POST /api/v1/reports"
|
@doc "POST /api/v1/reports"
|
||||||
def create(%{assigns: %{user: user}} = conn, params) do
|
def create(%{assigns: %{user: user}} = conn, params) do
|
||||||
with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do
|
with {:ok, activity} <- Pleroma.Web.CommonAPI.report(user, params) do
|
||||||
|
|
|
@ -18,8 +18,6 @@ defmodule Pleroma.Web.MastodonAPI.ScheduledActivityController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in @oauth_read_actions)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions)
|
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action not in @oauth_read_actions)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
@doc "GET /api/v1/scheduled_statuses"
|
@doc "GET /api/v1/scheduled_statuses"
|
||||||
|
|
|
@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
# Note: Mastodon doesn't allow unauthenticated access (requires read:accounts / read:search)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated})
|
plug(OAuthScopesPlug, %{scopes: ["read:search"], fallback: :proceed_unauthenticated})
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search])
|
plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search])
|
||||||
|
|
||||||
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
|
|
|
@ -77,7 +77,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
%{scopes: ["write:bookmarks"]} when action in [:bookmark, :unbookmark]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action not in [:index, :show])
|
plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show])
|
||||||
|
|
||||||
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
plug(:restrict_push_enabled)
|
plug(:restrict_push_enabled)
|
||||||
|
|
||||||
# Creates PushSubscription
|
# Creates PushSubscription
|
||||||
|
|
|
@ -26,7 +26,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
|
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action != :public)
|
plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :public)
|
||||||
|
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.ReverseProxy
|
alias Pleroma.ReverseProxy
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
plug(:skip_plug, OAuthScopesPlug when action == :confirmation_resend)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action == :confirmation_resend
|
||||||
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe]
|
%{scopes: ["follow", "write:follows"]} when action in [:subscribe, :unsubscribe]
|
||||||
|
@ -35,13 +42,8 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites)
|
plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites)
|
||||||
|
|
||||||
# An extra safety measure for possible actions not guarded by OAuth permissions specification
|
|
||||||
plug(
|
|
||||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
|
||||||
when action != :confirmation_resend
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
||||||
|
|
||||||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
@ -11,17 +12,20 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
||||||
when action in [
|
when action in [
|
||||||
:create,
|
:create,
|
||||||
:delete,
|
:delete,
|
||||||
:download_from,
|
:save_from,
|
||||||
:list_from,
|
|
||||||
:import_from_fs,
|
:import_from_fs,
|
||||||
:update_file,
|
:update_file,
|
||||||
:update_metadata
|
:update_metadata
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[OAuthScopesPlug, ExpectPublicOrAuthenticatedCheckPlug]
|
||||||
|
when action in [:download_shared, :list_packs, :list_from]
|
||||||
|
)
|
||||||
|
|
||||||
def emoji_dir_path do
|
defp emoji_dir_path do
|
||||||
Path.join(
|
Path.join(
|
||||||
Pleroma.Config.get!([:instance, :static_dir]),
|
Pleroma.Config.get!([:instance, :static_dir]),
|
||||||
"emoji"
|
"emoji"
|
||||||
|
@ -212,13 +216,13 @@ defp shareable_packs_available(address) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
An admin endpoint to request downloading a pack named `pack_name` from the instance
|
An admin endpoint to request downloading and storing a pack named `pack_name` from the instance
|
||||||
`instance_address`.
|
`instance_address`.
|
||||||
|
|
||||||
If the requested instance's admin chose to share the pack, it will be downloaded
|
If the requested instance's admin chose to share the pack, it will be downloaded
|
||||||
from that instance, otherwise it will be downloaded from the fallback source, if there is one.
|
from that instance, otherwise it will be downloaded from the fallback source, if there is one.
|
||||||
"""
|
"""
|
||||||
def download_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do
|
def save_from(conn, %{"instance_address" => address, "pack_name" => name} = data) do
|
||||||
address = String.trim(address)
|
address = String.trim(address)
|
||||||
|
|
||||||
if shareable_packs_available(address) do
|
if shareable_packs_available(address) do
|
||||||
|
|
|
@ -12,8 +12,6 @@ defmodule Pleroma.Web.PleromaAPI.MascotController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show)
|
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action == :show)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show)
|
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action != :show)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
@doc "GET /api/v1/pleroma/mascot"
|
@doc "GET /api/v1/pleroma/mascot"
|
||||||
def show(%{assigns: %{user: user}} = conn, _params) do
|
def show(%{assigns: %{user: user}} = conn, _params) do
|
||||||
json(conn, User.get_mascot(user))
|
json(conn, User.get_mascot(user))
|
||||||
|
|
|
@ -34,12 +34,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:conversations"]} when action in [:update_conversation, :read_conversations]
|
%{scopes: ["write:conversations"]}
|
||||||
|
when action in [:update_conversation, :mark_conversations_as_read]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :read_notification)
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
%{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
|
||||||
|
)
|
||||||
|
|
||||||
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
|
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||||
|
@ -167,7 +169,7 @@ def update_conversation(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_conversations(%{assigns: %{user: user}} = conn, _params) do
|
def mark_conversations_as_read(%{assigns: %{user: user}} = conn, _params) do
|
||||||
with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
|
with {:ok, _, participations} <- Participation.mark_all_as_read(user) do
|
||||||
conn
|
conn
|
||||||
|> add_link_headers(participations)
|
|> add_link_headers(participations)
|
||||||
|
@ -176,7 +178,7 @@ def read_conversations(%{assigns: %{user: user}} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
|
def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"id" => notification_id}) do
|
||||||
with {:ok, notification} <- Notification.read_one(user, notification_id) do
|
with {:ok, notification} <- Notification.read_one(user, notification_id) do
|
||||||
conn
|
conn
|
||||||
|> put_view(NotificationView)
|
|> put_view(NotificationView)
|
||||||
|
@ -189,7 +191,7 @@ def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def read_notification(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
|
def mark_notifications_as_read(%{assigns: %{user: user}} = conn, %{"max_id" => max_id} = params) do
|
||||||
with notifications <- Notification.set_read_up_to(user, max_id) do
|
with notifications <- Notification.set_read_up_to(user, max_id) do
|
||||||
notifications = Enum.take(notifications, 80)
|
notifications = Enum.take(notifications, 80)
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,6 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles)
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
||||||
params =
|
params =
|
||||||
if !params["length"] do
|
if !params["length"] do
|
||||||
|
|
|
@ -16,6 +16,14 @@ defmodule Pleroma.Web.Router do
|
||||||
plug(Pleroma.Plugs.UserEnabledPlug)
|
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
pipeline :expect_authentication do
|
||||||
|
plug(Pleroma.Plugs.ExpectAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :expect_public_instance_or_authentication do
|
||||||
|
plug(Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug)
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :authenticate do
|
pipeline :authenticate do
|
||||||
plug(Pleroma.Plugs.OAuthPlug)
|
plug(Pleroma.Plugs.OAuthPlug)
|
||||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||||
|
@ -39,20 +47,22 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :api do
|
pipeline :api do
|
||||||
|
plug(:expect_public_instance_or_authentication)
|
||||||
plug(:base_api)
|
plug(:base_api)
|
||||||
plug(:after_auth)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :authenticated_api do
|
pipeline :authenticated_api do
|
||||||
|
plug(:expect_authentication)
|
||||||
plug(:base_api)
|
plug(:base_api)
|
||||||
plug(Pleroma.Plugs.AuthExpectedPlug)
|
|
||||||
plug(:after_auth)
|
plug(:after_auth)
|
||||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :admin_api do
|
pipeline :admin_api do
|
||||||
|
plug(:expect_authentication)
|
||||||
plug(:base_api)
|
plug(:base_api)
|
||||||
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
||||||
plug(:after_auth)
|
plug(:after_auth)
|
||||||
|
@ -200,24 +210,28 @@ defmodule Pleroma.Web.Router do
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
# Modifying packs
|
||||||
scope "/packs" do
|
scope "/packs" do
|
||||||
# Modifying packs
|
|
||||||
pipe_through(:admin_api)
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
post("/import_from_fs", EmojiAPIController, :import_from_fs)
|
post("/import_from_fs", EmojiAPIController, :import_from_fs)
|
||||||
|
|
||||||
post("/:pack_name/update_file", EmojiAPIController, :update_file)
|
post("/:pack_name/update_file", EmojiAPIController, :update_file)
|
||||||
post("/:pack_name/update_metadata", EmojiAPIController, :update_metadata)
|
post("/:pack_name/update_metadata", EmojiAPIController, :update_metadata)
|
||||||
put("/:name", EmojiAPIController, :create)
|
put("/:name", EmojiAPIController, :create)
|
||||||
delete("/:name", EmojiAPIController, :delete)
|
delete("/:name", EmojiAPIController, :delete)
|
||||||
post("/download_from", EmojiAPIController, :download_from)
|
|
||||||
post("/list_from", EmojiAPIController, :list_from)
|
# Note: /download_from downloads and saves to instance, not to requester
|
||||||
|
post("/download_from", EmojiAPIController, :save_from)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Pack info / downloading
|
||||||
scope "/packs" do
|
scope "/packs" do
|
||||||
# Pack info / downloading
|
|
||||||
get("/", EmojiAPIController, :list_packs)
|
get("/", EmojiAPIController, :list_packs)
|
||||||
get("/:name/download_shared/", EmojiAPIController, :download_shared)
|
get("/:name/download_shared/", EmojiAPIController, :download_shared)
|
||||||
|
get("/list_from", EmojiAPIController, :list_from)
|
||||||
|
|
||||||
|
# Deprecated: POST /api/pleroma/emoji/packs/list_from (use GET instead)
|
||||||
|
post("/list_from", EmojiAPIController, :list_from)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -277,7 +291,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
|
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
|
||||||
get("/conversations/:id", PleromaAPIController, :conversation)
|
get("/conversations/:id", PleromaAPIController, :conversation)
|
||||||
post("/conversations/read", PleromaAPIController, :read_conversations)
|
post("/conversations/read", PleromaAPIController, :mark_conversations_as_read)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
|
@ -286,7 +300,7 @@ defmodule Pleroma.Web.Router do
|
||||||
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
||||||
put("/statuses/:id/reactions/:emoji", PleromaAPIController, :react_with_emoji)
|
put("/statuses/:id/reactions/:emoji", PleromaAPIController, :react_with_emoji)
|
||||||
delete("/statuses/:id/reactions/:emoji", PleromaAPIController, :unreact_with_emoji)
|
delete("/statuses/:id/reactions/:emoji", PleromaAPIController, :unreact_with_emoji)
|
||||||
post("/notifications/read", PleromaAPIController, :read_notification)
|
post("/notifications/read", PleromaAPIController, :mark_notifications_as_read)
|
||||||
|
|
||||||
patch("/accounts/update_avatar", AccountController, :update_avatar)
|
patch("/accounts/update_avatar", AccountController, :update_avatar)
|
||||||
patch("/accounts/update_banner", AccountController, :update_banner)
|
patch("/accounts/update_banner", AccountController, :update_banner)
|
||||||
|
@ -322,53 +336,81 @@ defmodule Pleroma.Web.Router do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
get("/accounts/verify_credentials", AccountController, :verify_credentials)
|
get("/accounts/verify_credentials", AccountController, :verify_credentials)
|
||||||
|
patch("/accounts/update_credentials", AccountController, :update_credentials)
|
||||||
|
|
||||||
get("/accounts/relationships", AccountController, :relationships)
|
get("/accounts/relationships", AccountController, :relationships)
|
||||||
|
|
||||||
get("/accounts/:id/lists", AccountController, :lists)
|
get("/accounts/:id/lists", AccountController, :lists)
|
||||||
get("/accounts/:id/identity_proofs", AccountController, :identity_proofs)
|
get("/accounts/:id/identity_proofs", AccountController, :identity_proofs)
|
||||||
|
get("/endorsements", AccountController, :endorsements)
|
||||||
get("/follow_requests", FollowRequestController, :index)
|
|
||||||
get("/blocks", AccountController, :blocks)
|
get("/blocks", AccountController, :blocks)
|
||||||
get("/mutes", AccountController, :mutes)
|
get("/mutes", AccountController, :mutes)
|
||||||
|
|
||||||
get("/timelines/home", TimelineController, :home)
|
post("/follows", AccountController, :follow_by_uri)
|
||||||
get("/timelines/direct", TimelineController, :direct)
|
post("/accounts/:id/follow", AccountController, :follow)
|
||||||
|
post("/accounts/:id/unfollow", AccountController, :unfollow)
|
||||||
|
post("/accounts/:id/block", AccountController, :block)
|
||||||
|
post("/accounts/:id/unblock", AccountController, :unblock)
|
||||||
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
|
|
||||||
get("/favourites", StatusController, :favourites)
|
get("/conversations", ConversationController, :index)
|
||||||
get("/bookmarks", StatusController, :bookmarks)
|
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
||||||
|
|
||||||
|
get("/domain_blocks", DomainBlockController, :index)
|
||||||
|
post("/domain_blocks", DomainBlockController, :create)
|
||||||
|
delete("/domain_blocks", DomainBlockController, :delete)
|
||||||
|
|
||||||
|
get("/filters", FilterController, :index)
|
||||||
|
|
||||||
|
post("/filters", FilterController, :create)
|
||||||
|
get("/filters/:id", FilterController, :show)
|
||||||
|
put("/filters/:id", FilterController, :update)
|
||||||
|
delete("/filters/:id", FilterController, :delete)
|
||||||
|
|
||||||
|
get("/follow_requests", FollowRequestController, :index)
|
||||||
|
post("/follow_requests/:id/authorize", FollowRequestController, :authorize)
|
||||||
|
post("/follow_requests/:id/reject", FollowRequestController, :reject)
|
||||||
|
|
||||||
|
get("/lists", ListController, :index)
|
||||||
|
get("/lists/:id", ListController, :show)
|
||||||
|
get("/lists/:id/accounts", ListController, :list_accounts)
|
||||||
|
|
||||||
|
delete("/lists/:id", ListController, :delete)
|
||||||
|
post("/lists", ListController, :create)
|
||||||
|
put("/lists/:id", ListController, :update)
|
||||||
|
post("/lists/:id/accounts", ListController, :add_to_list)
|
||||||
|
delete("/lists/:id/accounts", ListController, :remove_from_list)
|
||||||
|
|
||||||
|
get("/markers", MarkerController, :index)
|
||||||
|
post("/markers", MarkerController, :upsert)
|
||||||
|
|
||||||
|
post("/media", MediaController, :create)
|
||||||
|
put("/media/:id", MediaController, :update)
|
||||||
|
|
||||||
get("/notifications", NotificationController, :index)
|
get("/notifications", NotificationController, :index)
|
||||||
get("/notifications/:id", NotificationController, :show)
|
get("/notifications/:id", NotificationController, :show)
|
||||||
|
|
||||||
post("/notifications/:id/dismiss", NotificationController, :dismiss)
|
post("/notifications/:id/dismiss", NotificationController, :dismiss)
|
||||||
post("/notifications/clear", NotificationController, :clear)
|
post("/notifications/clear", NotificationController, :clear)
|
||||||
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
|
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
|
||||||
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
|
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
|
||||||
post("/notifications/dismiss", NotificationController, :dismiss)
|
post("/notifications/dismiss", NotificationController, :dismiss)
|
||||||
|
|
||||||
|
post("/polls/:id/votes", PollController, :vote)
|
||||||
|
|
||||||
|
post("/reports", ReportController, :create)
|
||||||
|
|
||||||
get("/scheduled_statuses", ScheduledActivityController, :index)
|
get("/scheduled_statuses", ScheduledActivityController, :index)
|
||||||
get("/scheduled_statuses/:id", ScheduledActivityController, :show)
|
get("/scheduled_statuses/:id", ScheduledActivityController, :show)
|
||||||
|
|
||||||
get("/lists", ListController, :index)
|
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
|
||||||
get("/lists/:id", ListController, :show)
|
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
|
||||||
get("/lists/:id/accounts", ListController, :list_accounts)
|
|
||||||
|
|
||||||
get("/domain_blocks", DomainBlockController, :index)
|
get("/favourites", StatusController, :favourites)
|
||||||
|
get("/bookmarks", StatusController, :bookmarks)
|
||||||
get("/filters", FilterController, :index)
|
|
||||||
|
|
||||||
get("/suggestions", SuggestionController, :index)
|
|
||||||
|
|
||||||
get("/conversations", ConversationController, :index)
|
|
||||||
post("/conversations/:id/read", ConversationController, :read)
|
|
||||||
|
|
||||||
get("/endorsements", AccountController, :endorsements)
|
|
||||||
|
|
||||||
patch("/accounts/update_credentials", AccountController, :update_credentials)
|
|
||||||
|
|
||||||
post("/statuses", StatusController, :create)
|
post("/statuses", StatusController, :create)
|
||||||
delete("/statuses/:id", StatusController, :delete)
|
delete("/statuses/:id", StatusController, :delete)
|
||||||
|
|
||||||
post("/statuses/:id/reblog", StatusController, :reblog)
|
post("/statuses/:id/reblog", StatusController, :reblog)
|
||||||
post("/statuses/:id/unreblog", StatusController, :unreblog)
|
post("/statuses/:id/unreblog", StatusController, :unreblog)
|
||||||
post("/statuses/:id/favourite", StatusController, :favourite)
|
post("/statuses/:id/favourite", StatusController, :favourite)
|
||||||
|
@ -380,49 +422,15 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/statuses/:id/mute", StatusController, :mute_conversation)
|
post("/statuses/:id/mute", StatusController, :mute_conversation)
|
||||||
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
post("/statuses/:id/unmute", StatusController, :unmute_conversation)
|
||||||
|
|
||||||
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
|
|
||||||
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
|
|
||||||
|
|
||||||
post("/polls/:id/votes", PollController, :vote)
|
|
||||||
|
|
||||||
post("/media", MediaController, :create)
|
|
||||||
put("/media/:id", MediaController, :update)
|
|
||||||
|
|
||||||
delete("/lists/:id", ListController, :delete)
|
|
||||||
post("/lists", ListController, :create)
|
|
||||||
put("/lists/:id", ListController, :update)
|
|
||||||
|
|
||||||
post("/lists/:id/accounts", ListController, :add_to_list)
|
|
||||||
delete("/lists/:id/accounts", ListController, :remove_from_list)
|
|
||||||
|
|
||||||
post("/filters", FilterController, :create)
|
|
||||||
get("/filters/:id", FilterController, :show)
|
|
||||||
put("/filters/:id", FilterController, :update)
|
|
||||||
delete("/filters/:id", FilterController, :delete)
|
|
||||||
|
|
||||||
post("/reports", ReportController, :create)
|
|
||||||
|
|
||||||
post("/follows", AccountController, :follows)
|
|
||||||
post("/accounts/:id/follow", AccountController, :follow)
|
|
||||||
post("/accounts/:id/unfollow", AccountController, :unfollow)
|
|
||||||
post("/accounts/:id/block", AccountController, :block)
|
|
||||||
post("/accounts/:id/unblock", AccountController, :unblock)
|
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
|
||||||
|
|
||||||
post("/follow_requests/:id/authorize", FollowRequestController, :authorize)
|
|
||||||
post("/follow_requests/:id/reject", FollowRequestController, :reject)
|
|
||||||
|
|
||||||
post("/domain_blocks", DomainBlockController, :create)
|
|
||||||
delete("/domain_blocks", DomainBlockController, :delete)
|
|
||||||
|
|
||||||
post("/push/subscription", SubscriptionController, :create)
|
post("/push/subscription", SubscriptionController, :create)
|
||||||
get("/push/subscription", SubscriptionController, :get)
|
get("/push/subscription", SubscriptionController, :get)
|
||||||
put("/push/subscription", SubscriptionController, :update)
|
put("/push/subscription", SubscriptionController, :update)
|
||||||
delete("/push/subscription", SubscriptionController, :delete)
|
delete("/push/subscription", SubscriptionController, :delete)
|
||||||
|
|
||||||
get("/markers", MarkerController, :index)
|
get("/suggestions", SuggestionController, :index)
|
||||||
post("/markers", MarkerController, :upsert)
|
|
||||||
|
get("/timelines/home", TimelineController, :home)
|
||||||
|
get("/timelines/direct", TimelineController, :direct)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/web", Pleroma.Web do
|
scope "/api/web", Pleroma.Web do
|
||||||
|
@ -507,7 +515,11 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
|
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
|
||||||
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
|
delete("/oauth_tokens/:id", TwitterAPI.Controller, :revoke_token)
|
||||||
|
|
||||||
post("/qvitter/statuses/notifications/read", TwitterAPI.Controller, :notifications_read)
|
post(
|
||||||
|
"/qvitter/statuses/notifications/read",
|
||||||
|
TwitterAPI.Controller,
|
||||||
|
:mark_notifications_as_read
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :ostatus do
|
pipeline :ostatus do
|
||||||
|
|
|
@ -25,13 +25,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
when action == :follow_import
|
when action == :follow_import
|
||||||
)
|
)
|
||||||
|
|
||||||
# Note: follower can submit the form (with password auth) not being signed in (having no token)
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{fallback: :proceed_unauthenticated, scopes: ["follow", "write:follows"]}
|
|
||||||
when action == :do_remote_follow
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:blocks"]} when action == :blocks_import)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
|
|
@ -13,12 +13,13 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:notifications"]} when action == :notifications_read)
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
|
||||||
|
)
|
||||||
|
|
||||||
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
|
||||||
|
|
||||||
action_fallback(:errors)
|
action_fallback(:errors)
|
||||||
|
|
||||||
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
def confirm_email(conn, %{"user_id" => uid, "token" => token}) do
|
||||||
|
@ -64,7 +65,10 @@ defp json_reply(conn, status, json) do
|
||||||
|> send_resp(status, json)
|
|> send_resp(status, json)
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest_id} = params) do
|
def mark_notifications_as_read(
|
||||||
|
%{assigns: %{user: user}} = conn,
|
||||||
|
%{"latest_id" => latest_id} = params
|
||||||
|
) do
|
||||||
Notification.set_read_up_to(user, latest_id)
|
Notification.set_read_up_to(user, latest_id)
|
||||||
|
|
||||||
notifications = Notification.for_user(user, params)
|
notifications = Notification.for_user(user, params)
|
||||||
|
@ -75,7 +79,7 @@ def notifications_read(%{assigns: %{user: user}} = conn, %{"latest_id" => latest
|
||||||
|> render("index.json", %{notifications: notifications, for: user})
|
|> render("index.json", %{notifications: notifications, for: user})
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications_read(%{assigns: %{user: _user}} = conn, _) do
|
def mark_notifications_as_read(%{assigns: %{user: _user}} = conn, _) do
|
||||||
bad_request_reply(conn, "You need to specify latest_id")
|
bad_request_reply(conn, "You need to specify latest_id")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Plug do
|
||||||
|
# Substitute for `call/2` which is defined with `use Pleroma.Web, :plug`
|
||||||
|
@callback perform(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
|
||||||
|
end
|
||||||
|
|
||||||
defmodule Pleroma.Web do
|
defmodule Pleroma.Web do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
A module that keeps using definitions for controllers,
|
A module that keeps using definitions for controllers,
|
||||||
|
@ -20,44 +25,79 @@ defmodule Pleroma.Web do
|
||||||
below.
|
below.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.EnsureAuthenticatedPlug
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
|
alias Pleroma.Plugs.ExpectAuthenticatedCheckPlug
|
||||||
|
alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Plugs.PlugHelper
|
||||||
|
|
||||||
def controller do
|
def controller do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.Controller, namespace: Pleroma.Web
|
use Phoenix.Controller, namespace: Pleroma.Web
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
import Pleroma.Web.Router.Helpers
|
import Pleroma.Web.Router.Helpers
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
|
||||||
alias Pleroma.Plugs.PlugHelper
|
|
||||||
|
|
||||||
plug(:set_put_layout)
|
plug(:set_put_layout)
|
||||||
|
|
||||||
defp set_put_layout(conn, _) do
|
defp set_put_layout(conn, _) do
|
||||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain
|
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
||||||
defp skip_plug(conn, plug_module) do
|
defp skip_plug(conn, plug_modules) do
|
||||||
try do
|
plug_modules
|
||||||
plug_module.skip_plug(conn)
|
|> List.wrap()
|
||||||
rescue
|
|> Enum.reduce(
|
||||||
UndefinedFunctionError ->
|
conn,
|
||||||
raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
fn plug_module, conn ->
|
||||||
end
|
try do
|
||||||
|
plug_module.skip_plug(conn)
|
||||||
|
rescue
|
||||||
|
UndefinedFunctionError ->
|
||||||
|
raise "`#{plug_module}` is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
||||||
defp action(conn, params) do
|
defp action(conn, params) do
|
||||||
with %Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do
|
with %Plug.Conn{halted: false} <- maybe_perform_public_or_authenticated_check(conn),
|
||||||
|
%Plug.Conn{halted: false} <- maybe_perform_authenticated_check(conn),
|
||||||
|
%Plug.Conn{halted: false} <- maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||||
super(conn, params)
|
super(conn, params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Ensures instance is public -or- user is authenticated if such check was scheduled
|
||||||
|
defp maybe_perform_public_or_authenticated_check(conn) do
|
||||||
|
if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) do
|
||||||
|
EnsurePublicOrAuthenticatedPlug.call(conn, %{})
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ensures user is authenticated if such check was scheduled
|
||||||
|
# Note: runs prior to action even if it was already executed earlier in plug chain
|
||||||
|
# (since OAuthScopesPlug has option of proceeding unauthenticated)
|
||||||
|
defp maybe_perform_authenticated_check(conn) do
|
||||||
|
if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) do
|
||||||
|
EnsureAuthenticatedPlug.call(conn, %{})
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
|
# Halts if authenticated API action neither performs nor explicitly skips OAuth scopes check
|
||||||
defp maybe_halt_on_missing_oauth_scopes_check(conn) do
|
defp maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||||
if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
|
if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and
|
||||||
not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
|
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
||||||
conn
|
conn
|
||||||
|> render_error(
|
|> render_error(
|
||||||
:forbidden,
|
:forbidden,
|
||||||
|
@ -132,7 +172,8 @@ def channel do
|
||||||
|
|
||||||
def plug do
|
def plug do
|
||||||
quote do
|
quote do
|
||||||
alias Pleroma.Plugs.PlugHelper
|
@behaviour Pleroma.Web.Plug
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
|
Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain.
|
||||||
|
@ -146,14 +187,22 @@ def skip_plug(conn) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl Plug
|
@impl Plug
|
||||||
@doc "If marked as skipped, returns `conn`, and calls `perform/2` otherwise."
|
@doc """
|
||||||
|
If marked as skipped, returns `conn`, otherwise calls `perform/2`.
|
||||||
|
Note: multiple invocations of the same plug (with different or same options) are allowed.
|
||||||
|
"""
|
||||||
def call(%Plug.Conn{} = conn, options) do
|
def call(%Plug.Conn{} = conn, options) do
|
||||||
if PlugHelper.plug_skipped?(conn, __MODULE__) do
|
if PlugHelper.plug_skipped?(conn, __MODULE__) do
|
||||||
conn
|
conn
|
||||||
else
|
else
|
||||||
conn
|
conn =
|
||||||
|> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__)
|
PlugHelper.append_to_private_list(
|
||||||
|> perform(options)
|
conn,
|
||||||
|
PlugHelper.called_plugs_list_id(),
|
||||||
|
__MODULE__
|
||||||
|
)
|
||||||
|
|
||||||
|
apply(__MODULE__, :perform, [conn, options])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,7 +20,7 @@ test "it continues if a user is assigned", %{conn: conn} do
|
||||||
conn = assign(conn, :user, %User{})
|
conn = assign(conn, :user, %User{})
|
||||||
ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
|
ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
|
||||||
|
|
||||||
assert ret_conn == conn
|
refute ret_conn.halted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,20 +34,22 @@ test "it continues if a user is assigned", %{conn: conn} do
|
||||||
|
|
||||||
test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do
|
test "it continues if a user is assigned", %{conn: conn, true_fn: true_fn, false_fn: false_fn} do
|
||||||
conn = assign(conn, :user, %User{})
|
conn = assign(conn, :user, %User{})
|
||||||
assert EnsureAuthenticatedPlug.call(conn, if_func: true_fn) == conn
|
refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted
|
||||||
assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn
|
refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted
|
||||||
assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn
|
refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted
|
||||||
assert EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) == conn
|
refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
|
test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
|
||||||
%{conn: conn, false_fn: false_fn} do
|
%{conn: conn, false_fn: false_fn} do
|
||||||
assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn
|
ret_conn = EnsureAuthenticatedPlug.call(conn, if_func: false_fn)
|
||||||
|
refute ret_conn.halted
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
|
test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
|
||||||
%{conn: conn, true_fn: true_fn} do
|
%{conn: conn, true_fn: true_fn} do
|
||||||
assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn
|
ret_conn = EnsureAuthenticatedPlug.call(conn, unless_func: true_fn)
|
||||||
|
refute ret_conn.halted
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it halts if a user is NOT assigned and :if_func evaluates to `true`",
|
test "it halts if a user is NOT assigned and :if_func evaluates to `true`",
|
||||||
|
|
|
@ -29,7 +29,7 @@ test "it continues if public", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
||||||
|
|
||||||
assert ret_conn == conn
|
refute ret_conn.halted
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it continues if a user is assigned, even if not public", %{conn: conn} do
|
test "it continues if a user is assigned, even if not public", %{conn: conn} do
|
||||||
|
@ -43,6 +43,6 @@ test "it continues if a user is assigned, even if not public", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
||||||
|
|
||||||
assert ret_conn == conn
|
refute ret_conn.halted
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,17 +5,12 @@
|
||||||
defmodule Pleroma.Plugs.OAuthScopesPlugTest do
|
defmodule Pleroma.Plugs.OAuthScopesPlugTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
|
|
||||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
import Mock
|
import Mock
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_with_mocks([{EnsurePublicOrAuthenticatedPlug, [], [call: fn conn, _ -> conn end]}]) do
|
|
||||||
:ok
|
|
||||||
end
|
|
||||||
|
|
||||||
test "is not performed if marked as skipped", %{conn: conn} do
|
test "is not performed if marked as skipped", %{conn: conn} do
|
||||||
with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
|
with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
|
||||||
conn =
|
conn =
|
||||||
|
@ -60,7 +55,7 @@ test "if `token.scopes` fulfills specified 'all of' conditions, " <>
|
||||||
|
|
||||||
describe "with `fallback: :proceed_unauthenticated` option, " do
|
describe "with `fallback: :proceed_unauthenticated` option, " do
|
||||||
test "if `token.scopes` doesn't fulfill specified conditions, " <>
|
test "if `token.scopes` doesn't fulfill specified conditions, " <>
|
||||||
"clears :user and :token assigns and calls EnsurePublicOrAuthenticatedPlug",
|
"clears :user and :token assigns",
|
||||||
%{conn: conn} do
|
%{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
token1 = insert(:oauth_token, scopes: ["read", "write"], user: user)
|
token1 = insert(:oauth_token, scopes: ["read", "write"], user: user)
|
||||||
|
@ -79,35 +74,6 @@ test "if `token.scopes` doesn't fulfill specified conditions, " <>
|
||||||
refute ret_conn.halted
|
refute ret_conn.halted
|
||||||
refute ret_conn.assigns[:user]
|
refute ret_conn.assigns[:user]
|
||||||
refute ret_conn.assigns[:token]
|
refute ret_conn.assigns[:token]
|
||||||
|
|
||||||
assert called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "with :skip_instance_privacy_check option, " <>
|
|
||||||
"if `token.scopes` doesn't fulfill specified conditions, " <>
|
|
||||||
"clears :user and :token assigns and does NOT call EnsurePublicOrAuthenticatedPlug",
|
|
||||||
%{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
token1 = insert(:oauth_token, scopes: ["read:statuses", "write"], user: user)
|
|
||||||
|
|
||||||
for token <- [token1, nil], op <- [:|, :&] do
|
|
||||||
ret_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> assign(:token, token)
|
|
||||||
|> OAuthScopesPlug.call(%{
|
|
||||||
scopes: ["read"],
|
|
||||||
op: op,
|
|
||||||
fallback: :proceed_unauthenticated,
|
|
||||||
skip_instance_privacy_check: true
|
|
||||||
})
|
|
||||||
|
|
||||||
refute ret_conn.halted
|
|
||||||
refute ret_conn.assigns[:user]
|
|
||||||
refute ret_conn.assigns[:token]
|
|
||||||
|
|
||||||
refute called(EnsurePublicOrAuthenticatedPlug.call(ret_conn, :_))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -766,7 +766,7 @@ test "it requires authentication if instance is NOT federating", %{
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /users/:nickname/outbox" do
|
describe "POST /users/:nickname/outbox" do
|
||||||
test "it rejects posts from other users / unauuthenticated users", %{conn: conn} do
|
test "it rejects posts from other users / unauthenticated users", %{conn: conn} do
|
||||||
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
data = File.read!("test/fixtures/activitypub-client-post-activity.json") |> Poison.decode!()
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
|
@ -38,8 +38,7 @@ test "shared & non-shared pack information in list_packs is ok" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "listing remote packs" do
|
test "listing remote packs" do
|
||||||
admin = insert(:user, is_admin: true)
|
conn = build_conn()
|
||||||
%{conn: conn} = oauth_access(["admin:write"], user: admin)
|
|
||||||
|
|
||||||
resp =
|
resp =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -76,7 +75,7 @@ test "downloading a shared pack from download_shared" do
|
||||||
assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
|
assert Enum.find(arch, fn {n, _} -> n == 'blank.png' end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "downloading shared & unshared packs from another instance via download_from, deleting them" do
|
test "downloading shared & unshared packs from another instance, deleting them" do
|
||||||
on_exit(fn ->
|
on_exit(fn ->
|
||||||
File.rm_rf!("#{@emoji_dir_path}/test_pack2")
|
File.rm_rf!("#{@emoji_dir_path}/test_pack2")
|
||||||
File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2")
|
File.rm_rf!("#{@emoji_dir_path}/test_pack_nonshared2")
|
||||||
|
@ -136,7 +135,7 @@ test "downloading shared & unshared packs from another instance via download_fro
|
||||||
|> post(
|
|> post(
|
||||||
emoji_api_path(
|
emoji_api_path(
|
||||||
conn,
|
conn,
|
||||||
:download_from
|
:save_from
|
||||||
),
|
),
|
||||||
%{
|
%{
|
||||||
instance_address: "https://old-instance",
|
instance_address: "https://old-instance",
|
||||||
|
@ -152,7 +151,7 @@ test "downloading shared & unshared packs from another instance via download_fro
|
||||||
|> post(
|
|> post(
|
||||||
emoji_api_path(
|
emoji_api_path(
|
||||||
conn,
|
conn,
|
||||||
:download_from
|
:save_from
|
||||||
),
|
),
|
||||||
%{
|
%{
|
||||||
instance_address: "https://example.com",
|
instance_address: "https://example.com",
|
||||||
|
@ -179,7 +178,7 @@ test "downloading shared & unshared packs from another instance via download_fro
|
||||||
|> post(
|
|> post(
|
||||||
emoji_api_path(
|
emoji_api_path(
|
||||||
conn,
|
conn,
|
||||||
:download_from
|
:save_from
|
||||||
),
|
),
|
||||||
%{
|
%{
|
||||||
instance_address: "https://example.com",
|
instance_address: "https://example.com",
|
||||||
|
|
|
@ -19,13 +19,9 @@ test "without valid credentials", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with credentials, without any params" do
|
test "with credentials, without any params" do
|
||||||
%{user: current_user, conn: conn} =
|
%{conn: conn} = oauth_access(["write:notifications"])
|
||||||
oauth_access(["read:notifications", "write:notifications"])
|
|
||||||
|
|
||||||
conn =
|
conn = post(conn, "/api/qvitter/statuses/notifications/read")
|
||||||
conn
|
|
||||||
|> assign(:user, current_user)
|
|
||||||
|> post("/api/qvitter/statuses/notifications/read")
|
|
||||||
|
|
||||||
assert json_response(conn, 400) == %{
|
assert json_response(conn, 400) == %{
|
||||||
"error" => "You need to specify latest_id",
|
"error" => "You need to specify latest_id",
|
||||||
|
|
Loading…
Reference in New Issue