Merge branch 'automatic-authentication-and-instance-publicity-checks' into 'develop'
Automatic checks of authentication / authorization / instance publicity See merge request pleroma/pleroma!2409
This commit is contained in:
commit
58fded9858
|
@ -0,0 +1,23 @@
|
||||||
|
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, it'll be verified whether `OAuthScopesPlug` was called or explicitly skipped, and if it was not then auth information will be dropped for request. Then `EnsurePublicOrAuthenticatedPlug` will be called to ensure that either 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]
|
||||||
|
@ -31,10 +28,7 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
conn
|
conn
|
||||||
|
|
||||||
options[:fallback] == :proceed_unauthenticated ->
|
options[:fallback] == :proceed_unauthenticated ->
|
||||||
conn
|
drop_auth_info(conn)
|
||||||
|> assign(:user, nil)
|
|
||||||
|> assign(:token, nil)
|
|
||||||
|> maybe_perform_instance_privacy_check(options)
|
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
missing_scopes = scopes -- matched_scopes
|
missing_scopes = scopes -- matched_scopes
|
||||||
|
@ -50,6 +44,15 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "Drops authentication info from connection"
|
||||||
|
def drop_auth_info(conn) do
|
||||||
|
# To simplify debugging, setting a private variable on `conn` if auth info is dropped
|
||||||
|
conn
|
||||||
|
|> put_private(:authentication_ignored, true)
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> assign(:token, nil)
|
||||||
|
end
|
||||||
|
|
||||||
@doc "Filters descendants of supported scopes"
|
@doc "Filters descendants of supported scopes"
|
||||||
def filter_descendants(scopes, supported_scopes) do
|
def filter_descendants(scopes, supported_scopes) do
|
||||||
Enum.filter(
|
Enum.filter(
|
||||||
|
@ -71,12 +74,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
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
# A test controller reachable only in :test env.
|
||||||
|
defmodule Pleroma.Tests.AuthTestController do
|
||||||
|
@moduledoc false
|
||||||
|
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
# Serves only with proper OAuth token (:api and :authenticated_api)
|
||||||
|
# Skipping EnsurePublicOrAuthenticatedPlug has no effect in this case
|
||||||
|
#
|
||||||
|
# Suggested use case: all :authenticated_api endpoints (makes no sense for :api endpoints)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :do_oauth_check)
|
||||||
|
|
||||||
|
# Via :api, keeps :user if token has requested scopes (if :user is dropped, serves if public)
|
||||||
|
# Via :authenticated_api, serves if token is present and has requested scopes
|
||||||
|
#
|
||||||
|
# Suggested use case: vast majority of :api endpoints (no sense for :authenticated_api ones)
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read"], fallback: :proceed_unauthenticated}
|
||||||
|
when action == :fallback_oauth_check
|
||||||
|
)
|
||||||
|
|
||||||
|
# Keeps :user if present, executes regardless of token / token scopes
|
||||||
|
# Fails with no :user for :authenticated_api / no user for :api on private instance
|
||||||
|
# Note: EnsurePublicOrAuthenticatedPlug is not skipped (private instance fails on no :user)
|
||||||
|
# Note: Basic Auth processing results in :skip_plug call for OAuthScopesPlug
|
||||||
|
#
|
||||||
|
# Suggested use: suppressing OAuth checks for other auth mechanisms (like Basic Auth)
|
||||||
|
# For controller-level use, see :skip_oauth_skip_publicity_check instead
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
OAuthScopesPlug when action == :skip_oauth_check
|
||||||
|
)
|
||||||
|
|
||||||
|
# (Shouldn't be executed since the plug is skipped)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["admin"]} when action == :skip_oauth_check)
|
||||||
|
|
||||||
|
# Via :api, keeps :user if token has requested scopes, and continues with nil :user otherwise
|
||||||
|
# Via :authenticated_api, serves if token is present and has requested scopes
|
||||||
|
#
|
||||||
|
# Suggested use: as :fallback_oauth_check but open with nil :user for :api on private instances
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
EnsurePublicOrAuthenticatedPlug when action == :fallback_oauth_skip_publicity_check
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read"], fallback: :proceed_unauthenticated}
|
||||||
|
when action == :fallback_oauth_skip_publicity_check
|
||||||
|
)
|
||||||
|
|
||||||
|
# Via :api, keeps :user if present, serves regardless of token presence / scopes / :user presence
|
||||||
|
# Via :authenticated_api, serves if :user is set (regardless of token presence and its scopes)
|
||||||
|
#
|
||||||
|
# Suggested use: making an :api endpoint always accessible (e.g. email confirmation endpoint)
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||||
|
when action == :skip_oauth_skip_publicity_check
|
||||||
|
)
|
||||||
|
|
||||||
|
# Via :authenticated_api, always fails with 403 (endpoint is insecure)
|
||||||
|
# Via :api, drops :user if present and serves if public (private instance rejects on no user)
|
||||||
|
#
|
||||||
|
# Suggested use: none; please define OAuth rules for all :api / :authenticated_api endpoints
|
||||||
|
plug(:skip_plug, [] when action == :missing_oauth_check_definition)
|
||||||
|
|
||||||
|
def do_oauth_check(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
def fallback_oauth_check(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
def skip_oauth_check(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
def fallback_oauth_skip_publicity_check(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
def skip_oauth_skip_publicity_check(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
def missing_oauth_check_definition(conn, _params), do: conn_state(conn)
|
||||||
|
|
||||||
|
defp conn_state(%{assigns: %{user: %User{} = user}} = conn),
|
||||||
|
do: json(conn, %{user_id: user.id})
|
||||||
|
|
||||||
|
defp conn_state(conn), do: json(conn, %{user_id: nil})
|
||||||
|
end
|
|
@ -1,31 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
# A test controller reachable only in :test env.
|
|
||||||
# Serves to test OAuth scopes check skipping / enforcement.
|
|
||||||
defmodule Pleroma.Tests.OAuthTestController do
|
|
||||||
@moduledoc false
|
|
||||||
|
|
||||||
use Pleroma.Web, :controller
|
|
||||||
|
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
|
||||||
|
|
||||||
plug(:skip_plug, OAuthScopesPlug when action == :skipped_oauth)
|
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action != :missed_oauth)
|
|
||||||
|
|
||||||
def skipped_oauth(conn, _params) do
|
|
||||||
noop(conn)
|
|
||||||
end
|
|
||||||
|
|
||||||
def performed_oauth(conn, _params) do
|
|
||||||
noop(conn)
|
|
||||||
end
|
|
||||||
|
|
||||||
def missed_oauth(conn, _params) do
|
|
||||||
noop(conn)
|
|
||||||
end
|
|
||||||
|
|
||||||
defp noop(conn), do: json(conn, %{})
|
|
||||||
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"))
|
||||||
|
|
|
@ -294,13 +294,13 @@ def unblock_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def follows_operation do
|
def follow_by_uri_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["accounts"],
|
tags: ["accounts"],
|
||||||
summary: "Follows",
|
summary: "Follow by URI",
|
||||||
operationId: "AccountController.follows",
|
operationId: "AccountController.follows",
|
||||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||||
requestBody: request_body("Parameters", follows_request(), required: true),
|
requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Account", "application/json", AccountRelationship),
|
200 => Operation.response("Account", "application/json", AccountRelationship),
|
||||||
400 => Operation.response("Error", "application/json", ApiError),
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
@ -615,7 +615,7 @@ defp array_of_relationships do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp follows_request do
|
defp follow_by_uri_request do
|
||||||
%Schema{
|
%Schema{
|
||||||
title: "AccountFollowsRequest",
|
title: "AccountFollowsRequest",
|
||||||
description: "POST body for muting an account",
|
description: "POST body for muting an account",
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -5,19 +5,25 @@
|
||||||
defmodule Pleroma.Web.MastoFEController do
|
defmodule Pleroma.Web.MastoFEController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
|
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
|
||||||
|
|
||||||
# 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(:skip_plug, EnsurePublicOrAuthenticatedPlug when action == :index)
|
||||||
|
|
||||||
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,
|
||||||
|
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :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)
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
skip_relationships?: 1
|
skip_relationships?: 1
|
||||||
]
|
]
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -28,18 +29,26 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||||
|
|
||||||
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
|
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||||
|
|
||||||
|
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [: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, :followers, :following]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]}
|
||||||
|
when action == :statuses
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:accounts"]}
|
%{scopes: ["read:accounts"]}
|
||||||
when action in [:endorsements, :verify_credentials, :followers, :following]
|
when action in [:verify_credentials, :endorsements, :identity_proofs]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials)
|
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :update_credentials)
|
||||||
|
@ -58,21 +67,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
|
||||||
|
|
||||||
|
@ -378,7 +381,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(%{body_params: %{uri: uri}} = conn, _) do
|
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||||
case User.get_cached_by_nickname(uri) do
|
case User.get_cached_by_nickname(uri) do
|
||||||
%User{} = user ->
|
%User{} = user ->
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.AppController do
|
defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
|
@ -13,7 +14,14 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||||
|
when action == :create
|
||||||
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
plug(OpenApiSpex.Plug.CastAndValidate)
|
||||||
|
|
||||||
@local_mastodon_name "Mastodon-Local"
|
@local_mastodon_name "Mastodon-Local"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -7,6 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
|
||||||
|
|
||||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
plug(OpenApiSpex.Plug.CastAndValidate)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||||
|
when action == :index
|
||||||
|
)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.CustomEmojiOperation
|
||||||
|
|
||||||
def index(conn, _params) do
|
def index(conn, _params) 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)
|
||||||
|
|
|
@ -5,6 +5,12 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||||
|
when action in [:show, :peers]
|
||||||
|
)
|
||||||
|
|
||||||
@doc "GET /api/v1/instance"
|
@doc "GET /api/v1/instance"
|
||||||
def show(conn, _params) do
|
def show(conn, _params) do
|
||||||
render(conn, "show.json")
|
render(conn, "show.json")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -15,9 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
|
plug(
|
||||||
|
:skip_plug,
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||||
|
when action in [:empty_array, :empty_object]
|
||||||
|
)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
|
||||||
|
|
|
@ -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,7 +21,7 @@ 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)
|
# Note: on private instances auth is required (EnsurePublicOrAuthenticatedPlug is not skipped)
|
||||||
|
|
||||||
plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search])
|
plug(RateLimiter, [name: :search] when action in [:search, :search2, :account_search])
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||||
|
|
||||||
|
plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show])
|
||||||
|
|
||||||
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -77,8 +79,6 @@ 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])
|
|
||||||
|
|
||||||
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
@rate_limited_status_actions ~w(reblog unreblog favourite unfavourite create delete)a
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -358,7 +358,7 @@ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/favourites"
|
@doc "GET /api/v1/favourites"
|
||||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_favourites(
|
ActivityPub.fetch_favourites(
|
||||||
user,
|
user,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -9,11 +9,14 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1]
|
only: [add_link_headers: 2, add_link_headers: 3, truthy_param?: 1, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
|
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:public, :hashtag])
|
||||||
|
|
||||||
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
|
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
|
||||||
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
||||||
|
|
||||||
|
@ -26,7 +29,11 @@ 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(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
|
||||||
|
when action in [:public, :hashtag]
|
||||||
|
)
|
||||||
|
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
||||||
|
|
||||||
|
@ -94,7 +101,9 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
|
||||||
restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key])
|
restrict? = Pleroma.Config.get([:restrict_unauthenticated, :timelines, cfg_key])
|
||||||
|
|
||||||
if not (restrict? and is_nil(user)) do
|
if restrict? and is_nil(user) do
|
||||||
|
render_error(conn, :unauthorized, "authorization required for timeline view")
|
||||||
|
else
|
||||||
activities =
|
activities =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|
@ -112,12 +121,10 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
as: :activity,
|
as: :activity,
|
||||||
skip_relationships: skip_relationships?(params)
|
skip_relationships: skip_relationships?(params)
|
||||||
)
|
)
|
||||||
else
|
|
||||||
render_error(conn, :unauthorized, "authorization required for timeline view")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def hashtag_fetching(params, user, local_only) do
|
defp hashtag_fetching(params, user, local_only) do
|
||||||
tags =
|
tags =
|
||||||
[params["tag"], params["any"]]
|
[params["tag"], params["any"]]
|
||||||
|> List.flatten()
|
|> List.flatten()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
|
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
plug(:fetch_flash)
|
plug(:fetch_flash)
|
||||||
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
|
||||||
|
|
||||||
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug)
|
plug(:skip_plug, [Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug])
|
||||||
|
|
||||||
|
plug(RateLimiter, [name: :authentication] when action == :create_authorization)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]
|
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2, skip_relationships?: 1]
|
||||||
|
|
||||||
alias Ecto.Changeset
|
alias Ecto.Changeset
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Plugs.RateLimiter
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -17,6 +18,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
plug(
|
||||||
|
:skip_plug,
|
||||||
|
[OAuthScopesPlug, 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]
|
||||||
|
@ -33,15 +39,13 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:favourites"]} when action == :favourites)
|
|
||||||
|
|
||||||
# An extra safety measure for possible actions not guarded by OAuth permissions specification
|
|
||||||
plug(
|
plug(
|
||||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
OAuthScopesPlug,
|
||||||
when action != :confirmation_resend
|
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||||
)
|
)
|
||||||
|
|
||||||
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))
|
||||||
|
|
|
@ -26,6 +26,12 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
||||||
when action in [:conversation, :conversation_statuses]
|
when action in [:conversation, :conversation_statuses]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
|
||||||
|
when action == :emoji_reactions_by
|
||||||
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:statuses"]}
|
%{scopes: ["write:statuses"]}
|
||||||
|
@ -34,12 +40,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 +175,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 +184,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 +197,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)
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,12 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles)
|
plug(
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read"], fallback: :proceed_unauthenticated} when action == :user_scrobbles
|
||||||
|
)
|
||||||
|
|
||||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
||||||
|
|
||||||
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
def new_scrobble(%{assigns: %{user: user}} = conn, %{"title" => _} = params) do
|
||||||
params =
|
params =
|
||||||
|
|
|
@ -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
|
||||||
scope "/packs" do
|
|
||||||
# Modifying packs
|
# Modifying packs
|
||||||
|
scope "/packs" do
|
||||||
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
|
||||||
|
|
||||||
scope "/packs" do
|
|
||||||
# Pack info / downloading
|
# Pack info / downloading
|
||||||
|
scope "/packs" do
|
||||||
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,84 @@ 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("/apps/verify_credentials", AppController, :verify_credentials)
|
||||||
get("/bookmarks", StatusController, :bookmarks)
|
|
||||||
|
get("/conversations", ConversationController, :index)
|
||||||
|
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)
|
# Unlike `GET /api/v1/accounts/:id/favourites`, demands authentication
|
||||||
|
get("/favourites", StatusController, :favourites)
|
||||||
get("/filters", FilterController, :index)
|
get("/bookmarks", StatusController, :bookmarks)
|
||||||
|
|
||||||
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 +425,16 @@ 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)
|
||||||
|
get("/timelines/list/:list_id", TimelineController, :list)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/web", Pleroma.Web do
|
scope "/api/web", Pleroma.Web do
|
||||||
|
@ -434,15 +446,24 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
|
|
||||||
post("/accounts", AccountController, :create)
|
|
||||||
get("/accounts/search", SearchController, :account_search)
|
get("/accounts/search", SearchController, :account_search)
|
||||||
|
get("/search", SearchController, :search)
|
||||||
|
|
||||||
|
get("/accounts/:id/statuses", AccountController, :statuses)
|
||||||
|
get("/accounts/:id/followers", AccountController, :followers)
|
||||||
|
get("/accounts/:id/following", AccountController, :following)
|
||||||
|
get("/accounts/:id", AccountController, :show)
|
||||||
|
|
||||||
|
post("/accounts", AccountController, :create)
|
||||||
|
|
||||||
get("/instance", InstanceController, :show)
|
get("/instance", InstanceController, :show)
|
||||||
get("/instance/peers", InstanceController, :peers)
|
get("/instance/peers", InstanceController, :peers)
|
||||||
|
|
||||||
post("/apps", AppController, :create)
|
post("/apps", AppController, :create)
|
||||||
get("/apps/verify_credentials", AppController, :verify_credentials)
|
|
||||||
|
|
||||||
|
get("/statuses", StatusController, :index)
|
||||||
|
get("/statuses/:id", StatusController, :show)
|
||||||
|
get("/statuses/:id/context", StatusController, :context)
|
||||||
get("/statuses/:id/card", StatusController, :card)
|
get("/statuses/:id/card", StatusController, :card)
|
||||||
get("/statuses/:id/favourited_by", StatusController, :favourited_by)
|
get("/statuses/:id/favourited_by", StatusController, :favourited_by)
|
||||||
get("/statuses/:id/reblogged_by", StatusController, :reblogged_by)
|
get("/statuses/:id/reblogged_by", StatusController, :reblogged_by)
|
||||||
|
@ -453,20 +474,8 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/timelines/public", TimelineController, :public)
|
get("/timelines/public", TimelineController, :public)
|
||||||
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
get("/timelines/tag/:tag", TimelineController, :hashtag)
|
||||||
get("/timelines/list/:list_id", TimelineController, :list)
|
|
||||||
|
|
||||||
get("/statuses", StatusController, :index)
|
|
||||||
get("/statuses/:id", StatusController, :show)
|
|
||||||
get("/statuses/:id/context", StatusController, :context)
|
|
||||||
|
|
||||||
get("/polls/:id", PollController, :show)
|
get("/polls/:id", PollController, :show)
|
||||||
|
|
||||||
get("/accounts/:id/statuses", AccountController, :statuses)
|
|
||||||
get("/accounts/:id/followers", AccountController, :followers)
|
|
||||||
get("/accounts/:id/following", AccountController, :following)
|
|
||||||
get("/accounts/:id", AccountController, :show)
|
|
||||||
|
|
||||||
get("/search", SearchController, :search)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||||
|
@ -507,7 +516,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
|
||||||
|
@ -647,11 +660,28 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
# Test-only routes needed to test action dispatching and plug chain execution
|
# Test-only routes needed to test action dispatching and plug chain execution
|
||||||
if Pleroma.Config.get(:env) == :test do
|
if Pleroma.Config.get(:env) == :test do
|
||||||
|
@test_actions [
|
||||||
|
:do_oauth_check,
|
||||||
|
:fallback_oauth_check,
|
||||||
|
:skip_oauth_check,
|
||||||
|
:fallback_oauth_skip_publicity_check,
|
||||||
|
:skip_oauth_skip_publicity_check,
|
||||||
|
:missing_oauth_check_definition
|
||||||
|
]
|
||||||
|
|
||||||
|
scope "/test/api", Pleroma.Tests do
|
||||||
|
pipe_through(:api)
|
||||||
|
|
||||||
|
for action <- @test_actions do
|
||||||
|
get("/#{action}", AuthTestController, action)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
scope "/test/authenticated_api", Pleroma.Tests do
|
scope "/test/authenticated_api", Pleroma.Tests do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do
|
for action <- @test_actions do
|
||||||
get("/#{action}", OAuthTestController, action)
|
get("/#{action}", AuthTestController, action)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -13,12 +14,18 @@ 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, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email
|
||||||
|
)
|
||||||
|
|
||||||
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
|
||||||
|
@ -46,13 +53,13 @@ def revoke_token(%{assigns: %{user: user}} = conn, %{"id" => id} = _params) do
|
||||||
json_reply(conn, 201, "")
|
json_reply(conn, 201, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, {:param_cast, _}) do
|
defp errors(conn, {:param_cast, _}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(400)
|
|> put_status(400)
|
||||||
|> json("Invalid parameters")
|
|> json("Invalid parameters")
|
||||||
end
|
end
|
||||||
|
|
||||||
def errors(conn, _) do
|
defp errors(conn, _) do
|
||||||
conn
|
conn
|
||||||
|> put_status(500)
|
|> put_status(500)
|
||||||
|> json("Something went wrong")
|
|> json("Something went wrong")
|
||||||
|
@ -64,7 +71,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 +85,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,91 @@ 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
|
||||||
|
plug_modules
|
||||||
|
|> List.wrap()
|
||||||
|
|> Enum.reduce(
|
||||||
|
conn,
|
||||||
|
fn plug_module, conn ->
|
||||||
try do
|
try do
|
||||||
plug_module.skip_plug(conn)
|
plug_module.skip_plug(conn)
|
||||||
rescue
|
rescue
|
||||||
UndefinedFunctionError ->
|
UndefinedFunctionError ->
|
||||||
raise "#{plug_module} is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
raise "`#{plug_module}` is not skippable. Append `use Pleroma.Web, :plug` to its code."
|
||||||
end
|
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 %{halted: false} = conn <- maybe_drop_authentication_if_oauth_check_ignored(conn),
|
||||||
|
%{halted: false} = conn <- maybe_perform_public_or_authenticated_check(conn),
|
||||||
|
%{halted: false} = conn <- maybe_perform_authenticated_check(conn),
|
||||||
|
%{halted: false} = conn <- maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||||
super(conn, params)
|
super(conn, params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# For non-authenticated API actions, drops auth info if OAuth scopes check was ignored
|
||||||
|
# (neither performed nor explicitly skipped)
|
||||||
|
defp maybe_drop_authentication_if_oauth_check_ignored(conn) do
|
||||||
|
if PlugHelper.plug_called?(conn, ExpectPublicOrAuthenticatedCheckPlug) and
|
||||||
|
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
||||||
|
OAuthScopesPlug.drop_auth_info(conn)
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
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 +184,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 +199,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)
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Tests.AuthTestControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "do_oauth_check" do
|
||||||
|
test "serves with proper OAuth token (fulfilling requested scopes)" do
|
||||||
|
%{conn: good_token_conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/authenticated_api/do_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
# Unintended usage (:api) — use with :authenticated_api instead
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/api/do_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fails on no token / missing scope(s)" do
|
||||||
|
%{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/authenticated_api/do_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
bad_token_conn
|
||||||
|
|> assign(:token, nil)
|
||||||
|
|> get("/test/api/do_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "fallback_oauth_check" do
|
||||||
|
test "serves with proper OAuth token (fulfilling requested scopes)" do
|
||||||
|
%{conn: good_token_conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/api/fallback_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
# Unintended usage (:authenticated_api) — use with :api instead
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/authenticated_api/fallback_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "for :api on public instance, drops :user and renders on no token / missing scope(s)" do
|
||||||
|
clear_config([:instance, :public], true)
|
||||||
|
|
||||||
|
%{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/api/fallback_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> assign(:token, nil)
|
||||||
|
|> get("/test/api/fallback_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "for :api on private instance, fails on no token / missing scope(s)" do
|
||||||
|
clear_config([:instance, :public], false)
|
||||||
|
|
||||||
|
%{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/api/fallback_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
bad_token_conn
|
||||||
|
|> assign(:token, nil)
|
||||||
|
|> get("/test/api/fallback_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "skip_oauth_check" do
|
||||||
|
test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
%{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "serves via :api on public instance if :user is not set" do
|
||||||
|
clear_config([:instance, :public], true)
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
build_conn()
|
||||||
|
|> get("/test/api/skip_oauth_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
build_conn()
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fails on private instance if :user is not set" do
|
||||||
|
clear_config([:instance, :public], false)
|
||||||
|
|
||||||
|
build_conn()
|
||||||
|
|> get("/test/api/skip_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
|
||||||
|
build_conn()
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_check")
|
||||||
|
|> json_response(403)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "fallback_oauth_skip_publicity_check" do
|
||||||
|
test "serves with proper OAuth token (fulfilling requested scopes)" do
|
||||||
|
%{conn: good_token_conn, user: user} = oauth_access(["read"])
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/api/fallback_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
# Unintended usage (:authenticated_api)
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
good_token_conn
|
||||||
|
|> get("/test/authenticated_api/fallback_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "for :api on private / public instance, drops :user and renders on token issue" do
|
||||||
|
%{conn: bad_token_conn} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
for is_public <- [true, false] do
|
||||||
|
clear_config([:instance, :public], is_public)
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/api/fallback_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> assign(:token, nil)
|
||||||
|
|> get("/test/api/fallback_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "skip_oauth_skip_publicity_check" do
|
||||||
|
test "for :authenticated_api, serves if :user is set (regardless of token / token scopes)" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
%{conn: bad_token_conn, user: user} = oauth_access(["irrelevant_scope"])
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
bad_token_conn
|
||||||
|
|> get("/test/authenticated_api/skip_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "for :api, serves on private and public instances regardless of whether :user is set" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
for is_public <- [true, false] do
|
||||||
|
clear_config([:instance, :public], is_public)
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
build_conn()
|
||||||
|
|> get("/test/api/skip_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert %{"user_id" => user.id} ==
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/test/api/skip_oauth_skip_publicity_check")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "missing_oauth_check_definition" do
|
||||||
|
def test_missing_oauth_check_definition_failure(endpoint, expected_error) do
|
||||||
|
%{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
|
||||||
|
|
||||||
|
assert %{"error" => expected_error} ==
|
||||||
|
conn
|
||||||
|
|> get(endpoint)
|
||||||
|
|> json_response(403)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fails if served via :authenticated_api" do
|
||||||
|
test_missing_oauth_check_definition_failure(
|
||||||
|
"/test/authenticated_api/missing_oauth_check_definition",
|
||||||
|
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "fails if served via :api and the instance is private" do
|
||||||
|
clear_config([:instance, :public], false)
|
||||||
|
|
||||||
|
test_missing_oauth_check_definition_failure(
|
||||||
|
"/test/api/missing_oauth_check_definition",
|
||||||
|
"This resource requires authentication."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "succeeds with dropped :user if served via :api on public instance" do
|
||||||
|
%{conn: conn} = oauth_access(["read", "write", "follow", "push", "admin"])
|
||||||
|
|
||||||
|
assert %{"user_id" => nil} ==
|
||||||
|
conn
|
||||||
|
|> get("/test/api/missing_oauth_check_definition")
|
||||||
|
|> json_response(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,49 +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.Tests.OAuthTestControllerTest do
|
|
||||||
use Pleroma.Web.ConnCase
|
|
||||||
|
|
||||||
import Pleroma.Factory
|
|
||||||
|
|
||||||
setup %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
conn = assign(conn, :user, user)
|
|
||||||
%{conn: conn, user: user}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "missed_oauth", %{conn: conn} do
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> get("/test/authenticated_api/missed_oauth")
|
|
||||||
|> json_response(403)
|
|
||||||
|
|
||||||
assert res ==
|
|
||||||
%{
|
|
||||||
"error" =>
|
|
||||||
"Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "skipped_oauth", %{conn: conn} do
|
|
||||||
conn
|
|
||||||
|> assign(:token, nil)
|
|
||||||
|> get("/test/authenticated_api/skipped_oauth")
|
|
||||||
|> json_response(200)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "performed_oauth", %{user: user} do
|
|
||||||
%{conn: good_token_conn} = oauth_access(["read"], user: user)
|
|
||||||
|
|
||||||
good_token_conn
|
|
||||||
|> get("/test/authenticated_api/performed_oauth")
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
%{conn: bad_token_conn} = oauth_access(["follow"], user: user)
|
|
||||||
|
|
||||||
bad_token_conn
|
|
||||||
|> get("/test/authenticated_api/performed_oauth")
|
|
||||||
|> json_response(403)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -7,35 +7,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
||||||
|
|
||||||
describe "empty_array/2 (stubs)" do
|
describe "empty_array/2 (stubs)" do
|
||||||
test "GET /api/v1/accounts/:id/identity_proofs" do
|
test "GET /api/v1/accounts/:id/identity_proofs" do
|
||||||
%{user: user, conn: conn} = oauth_access(["n/a"])
|
%{user: user, conn: conn} = oauth_access(["read:accounts"])
|
||||||
|
|
||||||
res =
|
assert [] ==
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/accounts/#{user.id}/identity_proofs")
|
|> get("/api/v1/accounts/#{user.id}/identity_proofs")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert res == []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "GET /api/v1/endorsements" do
|
test "GET /api/v1/endorsements" do
|
||||||
%{conn: conn} = oauth_access(["read:accounts"])
|
%{conn: conn} = oauth_access(["read:accounts"])
|
||||||
|
|
||||||
res =
|
assert [] ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/endorsements")
|
|> get("/api/v1/endorsements")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert res == []
|
|
||||||
end
|
end
|
||||||
|
|
||||||
test "GET /api/v1/trends", %{conn: conn} do
|
test "GET /api/v1/trends", %{conn: conn} do
|
||||||
res =
|
assert [] ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/trends")
|
|> get("/api/v1/trends")
|
||||||
|> json_response(200)
|
|> json_response(200)
|
||||||
|
|
||||||
assert res == []
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -151,15 +151,18 @@ test "returns list of statuses favorited by specified user", %{
|
||||||
assert like["id"] == activity.id
|
assert like["id"] == activity.id
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return favorites for specified user_id when user is not logged in", %{
|
test "returns favorites for specified user_id when requester is not logged in", %{
|
||||||
user: user
|
user: user
|
||||||
} do
|
} do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
CommonAPI.favorite(user, activity.id)
|
CommonAPI.favorite(user, activity.id)
|
||||||
|
|
||||||
|
response =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|> json_response(403)
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert length(response) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns favorited DM only when user is logged in and he is one of recipients", %{
|
test "returns favorited DM only when user is logged in and he is one of recipients", %{
|
||||||
|
@ -185,9 +188,12 @@ test "returns favorited DM only when user is logged in and he is one of recipien
|
||||||
assert length(response) == 1
|
assert length(response) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
response =
|
||||||
build_conn()
|
build_conn()
|
||||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||||
|> json_response(403)
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert length(response) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not return others' favorited DM when user is not one of recipients", %{
|
test "does not return others' favorited DM when user is not one of recipients", %{
|
||||||
|
|
|
@ -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