forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
0069e78fa7
86 changed files with 3086 additions and 984 deletions
|
@ -4,7 +4,7 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
|
|||
|
||||
## Flake IDs
|
||||
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings
|
||||
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
|
||||
|
||||
## Attachment cap
|
||||
|
||||
|
@ -120,6 +120,18 @@ Accepts additional parameters:
|
|||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
|
||||
## DELETE `/api/v1/notifications/destroy_multiple`
|
||||
|
||||
An endpoint to delete multiple statuses by IDs.
|
||||
|
||||
Required parameters:
|
||||
|
||||
- `ids`: array of activity ids
|
||||
|
||||
Usage example: `DELETE /api/v1/notifications/destroy_multiple/?ids[]=1&ids[]=2`.
|
||||
|
||||
Returns on success: 200 OK `{}`
|
||||
|
||||
## POST `/api/v1/statuses`
|
||||
|
||||
Additional parameters can be added to the JSON body/Form data:
|
||||
|
|
23
docs/dev.md
Normal file
23
docs/dev.md
Normal file
|
@ -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
|
||||
import Plug.Conn
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
|
||||
alias Pleroma.User
|
||||
|
||||
use Pleroma.Web, :plug
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _) do
|
||||
@impl true
|
||||
def perform(%{assigns: %{user: %User{}}} = conn, _) do
|
||||
conn
|
||||
end
|
||||
|
||||
def call(conn, options) do
|
||||
def perform(conn, options) do
|
||||
perform =
|
||||
cond do
|
||||
options[:if_func] -> options[:if_func].()
|
||||
|
|
|
@ -5,14 +5,18 @@
|
|||
defmodule Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug do
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
use Pleroma.Web, :plug
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
|
||||
def call(conn, _) do
|
||||
@impl true
|
||||
def perform(conn, _) do
|
||||
public? = Config.get!([:instance, :public])
|
||||
|
||||
case {public?, conn} do
|
||||
|
|
20
lib/pleroma/plugs/expect_authenticated_check_plug.ex
Normal file
20
lib/pleroma/plugs/expect_authenticated_check_plug.ex
Normal file
|
@ -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
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
|
||||
use Pleroma.Web, :plug
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
def init(%{scopes: _} = options), do: options
|
||||
|
||||
@impl true
|
||||
def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
||||
op = options[:op] || :|
|
||||
token = assigns[:token]
|
||||
|
@ -31,10 +28,7 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
|||
conn
|
||||
|
||||
options[:fallback] == :proceed_unauthenticated ->
|
||||
conn
|
||||
|> assign(:user, nil)
|
||||
|> assign(:token, nil)
|
||||
|> maybe_perform_instance_privacy_check(options)
|
||||
drop_auth_info(conn)
|
||||
|
||||
true ->
|
||||
missing_scopes = scopes -- matched_scopes
|
||||
|
@ -50,6 +44,15 @@ def perform(%Plug.Conn{assigns: assigns} = conn, %{scopes: scopes} = options) do
|
|||
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"
|
||||
def filter_descendants(scopes, supported_scopes) do
|
||||
Enum.filter(
|
||||
|
@ -71,12 +74,4 @@ def transform_scopes(scopes, options) do
|
|||
scopes
|
||||
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
|
||||
|
|
93
lib/pleroma/tests/auth_test_controller.ex
Normal file
93
lib/pleroma/tests/auth_test_controller.ex
Normal file
|
@ -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
|
|
@ -825,7 +825,7 @@ defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
|||
end
|
||||
|
||||
defp exclude_visibility(query, %{"exclude_visibilities" => visibility})
|
||||
when visibility not in @valid_visibilities do
|
||||
when visibility not in [nil | @valid_visibilities] do
|
||||
Logger.error("Could not exclude visibility to #{visibility}")
|
||||
query
|
||||
end
|
||||
|
@ -1032,7 +1032,7 @@ defp restrict_media(_query, %{"only_media" => _val, "skip_preload" => true}) do
|
|||
raise "Can't use the child object without preloading!"
|
||||
end
|
||||
|
||||
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_media(query, %{"only_media" => val}) when val in [true, "true", "1"] do
|
||||
from(
|
||||
[_activity, object] in query,
|
||||
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
||||
|
@ -1041,7 +1041,7 @@ defp restrict_media(query, %{"only_media" => val}) when val == "true" or val ==
|
|||
|
||||
defp restrict_media(query, _), do: query
|
||||
|
||||
defp restrict_replies(query, %{"exclude_replies" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_replies(query, %{"exclude_replies" => val}) when val in [true, "true", "1"] do
|
||||
from(
|
||||
[_activity, object] in query,
|
||||
where: fragment("?->>'inReplyTo' is null", object.data)
|
||||
|
@ -1085,7 +1085,7 @@ defp restrict_replies(query, %{
|
|||
|
||||
defp restrict_replies(query, _), do: query
|
||||
|
||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do
|
||||
defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val in [true, "true", "1"] do
|
||||
from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data))
|
||||
end
|
||||
|
||||
|
@ -1164,7 +1164,12 @@ defp restrict_unlisted(query) do
|
|||
)
|
||||
end
|
||||
|
||||
defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do
|
||||
# TODO: when all endpoints migrated to OpenAPI compare `pinned` with `true` (boolean) only,
|
||||
# the same for `restrict_media/2`, `restrict_replies/2`, 'restrict_reblogs/2'
|
||||
# and `restrict_muted/2`
|
||||
|
||||
defp restrict_pinned(query, %{"pinned" => pinned, "pinned_activity_ids" => ids})
|
||||
when pinned in [true, "true", "1"] do
|
||||
from(activity in query, where: activity.id in ^ids)
|
||||
end
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
%{scopes: ["write:accounts"], admin: true}
|
||||
when action in [
|
||||
:get_password_reset,
|
||||
:force_password_reset,
|
||||
:user_delete,
|
||||
:users_create,
|
||||
:user_toggle_activation,
|
||||
|
@ -56,7 +57,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
:tag_users,
|
||||
:untag_users,
|
||||
:right_add,
|
||||
:right_add_multiple,
|
||||
:right_delete,
|
||||
:right_delete_multiple,
|
||||
:update_user_credentials
|
||||
]
|
||||
)
|
||||
|
@ -84,13 +87,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:reports"], admin: true}
|
||||
when action in [:reports_update]
|
||||
when action in [:reports_update, :report_notes_create, :report_notes_delete]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"], admin: true}
|
||||
when action == :list_user_statuses
|
||||
when action in [:list_statuses, :list_user_statuses, :list_instance_statuses]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
@ -102,13 +105,30 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{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(
|
||||
OAuthScopesPlug,
|
||||
%{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)
|
||||
|
@ -1103,25 +1123,25 @@ def stats(conn, _) do
|
|||
|> json(%{"status_visibility" => count})
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
defp errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|> json(dgettext("errors", "Not found"))
|
||||
end
|
||||
|
||||
def errors(conn, {:error, reason}) do
|
||||
defp errors(conn, {:error, reason}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(reason)
|
||||
end
|
||||
|
||||
def errors(conn, {:param_cast, _}) do
|
||||
defp errors(conn, {:param_cast, _}) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(dgettext("errors", "Invalid parameters"))
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
defp errors(conn, _) do
|
||||
conn
|
||||
|> put_status(:internal_server_error)
|
||||
|> json(dgettext("errors", "Something went wrong"))
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ApiSpec do
|
||||
alias OpenApiSpex.OpenApi
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.Endpoint
|
||||
alias Pleroma.Web.Router
|
||||
|
||||
|
@ -24,6 +25,13 @@ def spec do
|
|||
# populate the paths from a phoenix router
|
||||
paths: OpenApiSpex.Paths.from_router(Router),
|
||||
components: %OpenApiSpex.Components{
|
||||
parameters: %{
|
||||
"accountIdOrNickname" =>
|
||||
Operation.parameter(:id, :path, :string, "Account ID or nickname",
|
||||
example: "123",
|
||||
required: true
|
||||
)
|
||||
},
|
||||
securitySchemes: %{
|
||||
"oAuth" => %OpenApiSpex.SecurityScheme{
|
||||
type: "oauth2",
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Helpers do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
def request_body(description, schema_ref, opts \\ []) do
|
||||
media_types = ["application/json", "multipart/form-data", "application/x-www-form-urlencoded"]
|
||||
|
||||
|
@ -24,4 +27,23 @@ def request_body(description, schema_ref, opts \\ []) do
|
|||
required: opts[:required] || false
|
||||
}
|
||||
end
|
||||
|
||||
def pagination_params do
|
||||
[
|
||||
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
|
||||
Operation.parameter(:min_id, :query, :string, "Return the oldest items newer than this ID"),
|
||||
Operation.parameter(
|
||||
:since_id,
|
||||
:query,
|
||||
:string,
|
||||
"Return the newest items newer than this ID"
|
||||
),
|
||||
Operation.parameter(
|
||||
:limit,
|
||||
:query,
|
||||
%Schema{type: :integer, default: 20, maximum: 40},
|
||||
"Limit"
|
||||
)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
701
lib/pleroma/web/api_spec/operations/account_operation.ex
Normal file
701
lib/pleroma/web/api_spec/operations/account_operation.ex
Normal file
|
@ -0,0 +1,701 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Reference
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Status
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
@spec open_api_operation(atom) :: Operation.t()
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
@spec create_operation() :: Operation.t()
|
||||
def create_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Register an account",
|
||||
description:
|
||||
"Creates a user and account records. Returns an account access token for the app that initiated the request. The app should save this token for later, and should wait for the user to confirm their account by clicking a link in their email inbox.",
|
||||
operationId: "AccountController.create",
|
||||
requestBody: request_body("Parameters", create_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", create_response()),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
403 => Operation.response("Error", "application/json", ApiError),
|
||||
429 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def verify_credentials_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
description: "Test to make sure that the user token works.",
|
||||
summary: "Verify account credentials",
|
||||
operationId: "AccountController.verify_credentials",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def update_credentials_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Update account credentials",
|
||||
description: "Update the user's display and preferences.",
|
||||
operationId: "AccountController.update_credentials",
|
||||
security: [%{"oAuth" => ["write:accounts"]}],
|
||||
requestBody: request_body("Parameters", update_creadentials_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account),
|
||||
403 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def relationships_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Check relationships to other accounts",
|
||||
operationId: "AccountController.relationships",
|
||||
description: "Find out whether a given account is followed, blocked, muted, etc.",
|
||||
security: [%{"oAuth" => ["read:follows"]}],
|
||||
parameters: [
|
||||
Operation.parameter(
|
||||
:id,
|
||||
:query,
|
||||
%Schema{
|
||||
oneOf: [%Schema{type: :array, items: %Schema{type: :string}}, %Schema{type: :string}]
|
||||
},
|
||||
"Account IDs",
|
||||
example: "123"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", array_of_relationships())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def show_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Account",
|
||||
operationId: "AccountController.show",
|
||||
description: "View information about a profile.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", Account),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def statuses_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Statuses",
|
||||
operationId: "AccountController.statuses",
|
||||
description:
|
||||
"Statuses posted to the given account. Public (for public statuses only), or user token + `read:statuses` (for private statuses the user is authorized to see)",
|
||||
parameters:
|
||||
[
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
|
||||
Operation.parameter(:tagged, :query, :string, "With tag"),
|
||||
Operation.parameter(
|
||||
:only_media,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Include only statuses with media attached"
|
||||
),
|
||||
Operation.parameter(
|
||||
:with_muted,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Include statuses from muted acccounts."
|
||||
),
|
||||
Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
|
||||
Operation.parameter(
|
||||
:exclude_visibilities,
|
||||
:query,
|
||||
%Schema{type: :array, items: VisibilityScope},
|
||||
"Exclude visibilities"
|
||||
)
|
||||
] ++ pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Statuses", "application/json", array_of_statuses()),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def followers_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Followers",
|
||||
operationId: "AccountController.followers",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
description:
|
||||
"Accounts which follow the given account, if network is not hidden by the account owner.",
|
||||
parameters:
|
||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def following_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Following",
|
||||
operationId: "AccountController.following",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
description:
|
||||
"Accounts which the given account is following, if network is not hidden by the account owner.",
|
||||
parameters:
|
||||
[%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}] ++ pagination_params(),
|
||||
responses: %{200 => Operation.response("Accounts", "application/json", array_of_accounts())}
|
||||
}
|
||||
end
|
||||
|
||||
def lists_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Lists containing this account",
|
||||
operationId: "AccountController.lists",
|
||||
security: [%{"oAuth" => ["read:lists"]}],
|
||||
description: "User lists that you have added this account to.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{200 => Operation.response("Lists", "application/json", array_of_lists())}
|
||||
}
|
||||
end
|
||||
|
||||
def follow_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Follow",
|
||||
operationId: "AccountController.follow",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
description: "Follow the given account",
|
||||
parameters: [
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(
|
||||
:reblogs,
|
||||
:query,
|
||||
BooleanLike,
|
||||
"Receive this account's reblogs in home timeline? Defaults to true."
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unfollow_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unfollow",
|
||||
operationId: "AccountController.unfollow",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
description: "Unfollow the given account",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def mute_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Mute",
|
||||
operationId: "AccountController.mute",
|
||||
security: [%{"oAuth" => ["follow", "write:mutes"]}],
|
||||
requestBody: request_body("Parameters", mute_request()),
|
||||
description:
|
||||
"Mute the given account. Clients should filter statuses and notifications from this account, if received (e.g. due to a boost in the Home timeline).",
|
||||
parameters: [
|
||||
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
|
||||
Operation.parameter(
|
||||
:notifications,
|
||||
:query,
|
||||
%Schema{allOf: [BooleanLike], default: true},
|
||||
"Mute notifications in addition to statuses? Defaults to `true`."
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unmute_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unmute",
|
||||
operationId: "AccountController.unmute",
|
||||
security: [%{"oAuth" => ["follow", "write:mutes"]}],
|
||||
description: "Unmute the given account.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def block_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Block",
|
||||
operationId: "AccountController.block",
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
description:
|
||||
"Block the given account. Clients should filter statuses from this account if received (e.g. due to a boost in the Home timeline)",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def unblock_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Unblock",
|
||||
operationId: "AccountController.unblock",
|
||||
security: [%{"oAuth" => ["follow", "write:blocks"]}],
|
||||
description: "Unblock the given account.",
|
||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||
responses: %{
|
||||
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def follow_by_uri_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Follow by URI",
|
||||
operationId: "AccountController.follows",
|
||||
security: [%{"oAuth" => ["follow", "write:follows"]}],
|
||||
requestBody: request_body("Parameters", follow_by_uri_request(), required: true),
|
||||
responses: %{
|
||||
200 => Operation.response("Account", "application/json", AccountRelationship),
|
||||
400 => Operation.response("Error", "application/json", ApiError),
|
||||
404 => Operation.response("Error", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def mutes_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Muted accounts",
|
||||
operationId: "AccountController.mutes",
|
||||
description: "Accounts the user has muted.",
|
||||
security: [%{"oAuth" => ["follow", "read:mutes"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def blocks_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Blocked users",
|
||||
operationId: "AccountController.blocks",
|
||||
description: "View your blocks. See also accounts/:id/{block,unblock}",
|
||||
security: [%{"oAuth" => ["read:blocks"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def endorsements_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Endorsements",
|
||||
operationId: "AccountController.endorsements",
|
||||
description: "Not implemented",
|
||||
security: [%{"oAuth" => ["read:accounts"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empry array", "application/json", %Schema{type: :array})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def identity_proofs_operation do
|
||||
%Operation{
|
||||
tags: ["accounts"],
|
||||
summary: "Identity proofs",
|
||||
operationId: "AccountController.identity_proofs",
|
||||
description: "Not implemented",
|
||||
responses: %{
|
||||
200 => Operation.response("Empry array", "application/json", %Schema{type: :array})
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_request do
|
||||
%Schema{
|
||||
title: "AccountCreateRequest",
|
||||
description: "POST body for creating an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
reason: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"Text that will be reviewed by moderators if registrations require manual approval"
|
||||
},
|
||||
username: %Schema{type: :string, description: "The desired username for the account"},
|
||||
email: %Schema{
|
||||
type: :string,
|
||||
description:
|
||||
"The email address to be used for login. Required when `account_activation_required` is enabled.",
|
||||
format: :email
|
||||
},
|
||||
password: %Schema{
|
||||
type: :string,
|
||||
description: "The password to be used for login",
|
||||
format: :password
|
||||
},
|
||||
agreement: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"Whether the user agrees to the local rules, terms, and policies. These should be presented to the user in order to allow them to consent before setting this parameter to TRUE."
|
||||
},
|
||||
locale: %Schema{
|
||||
type: :string,
|
||||
description: "The language of the confirmation email that will be sent"
|
||||
},
|
||||
# Pleroma-specific properties:
|
||||
fullname: %Schema{type: :string, description: "Full name"},
|
||||
bio: %Schema{type: :string, description: "Bio", default: ""},
|
||||
captcha_solution: %Schema{
|
||||
type: :string,
|
||||
description: "Provider-specific captcha solution"
|
||||
},
|
||||
captcha_token: %Schema{type: :string, description: "Provider-specific captcha token"},
|
||||
captcha_answer_data: %Schema{type: :string, description: "Provider-specific captcha data"},
|
||||
token: %Schema{
|
||||
type: :string,
|
||||
description: "Invite token required when the registrations aren't public"
|
||||
}
|
||||
},
|
||||
required: [:username, :password, :agreement],
|
||||
example: %{
|
||||
"username" => "cofe",
|
||||
"email" => "cofe@example.com",
|
||||
"password" => "secret",
|
||||
"agreement" => "true",
|
||||
"bio" => "☕️"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp create_response do
|
||||
%Schema{
|
||||
title: "AccountCreateResponse",
|
||||
description: "Response schema for an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
token_type: %Schema{type: :string},
|
||||
access_token: %Schema{type: :string},
|
||||
scope: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
created_at: %Schema{type: :integer, format: :"date-time"}
|
||||
},
|
||||
example: %{
|
||||
"access_token" => "i9hAVVzGld86Pl5JtLtizKoXVvtTlSCJvwaugCxvZzk",
|
||||
"created_at" => 1_585_918_714,
|
||||
"scope" => ["read", "write", "follow", "push"],
|
||||
"token_type" => "Bearer"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp update_creadentials_request do
|
||||
%Schema{
|
||||
title: "AccountUpdateCredentialsRequest",
|
||||
description: "POST body for creating an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
bot: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether the account has a bot flag."
|
||||
},
|
||||
display_name: %Schema{
|
||||
type: :string,
|
||||
description: "The display name to use for the profile."
|
||||
},
|
||||
note: %Schema{type: :string, description: "The account bio."},
|
||||
avatar: %Schema{
|
||||
type: :string,
|
||||
description: "Avatar image encoded using multipart/form-data",
|
||||
format: :binary
|
||||
},
|
||||
header: %Schema{
|
||||
type: :string,
|
||||
description: "Header image encoded using multipart/form-data",
|
||||
format: :binary
|
||||
},
|
||||
locked: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether manual approval of follow requests is required."
|
||||
},
|
||||
fields_attributes: %Schema{
|
||||
oneOf: [
|
||||
%Schema{type: :array, items: attribute_field()},
|
||||
%Schema{type: :object, additionalProperties: %Schema{type: attribute_field()}}
|
||||
]
|
||||
},
|
||||
# NOTE: `source` field is not supported
|
||||
#
|
||||
# source: %Schema{
|
||||
# type: :object,
|
||||
# properties: %{
|
||||
# privacy: %Schema{type: :string},
|
||||
# sensitive: %Schema{type: :boolean},
|
||||
# language: %Schema{type: :string}
|
||||
# }
|
||||
# },
|
||||
|
||||
# Pleroma-specific fields
|
||||
no_rich_text: %Schema{
|
||||
type: :boolean,
|
||||
description: "html tags are stripped from all statuses requested from the API"
|
||||
},
|
||||
hide_followers: %Schema{type: :boolean, description: "user's followers will be hidden"},
|
||||
hide_follows: %Schema{type: :boolean, description: "user's follows will be hidden"},
|
||||
hide_followers_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's follower count will be hidden"
|
||||
},
|
||||
hide_follows_count: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's follow count will be hidden"
|
||||
},
|
||||
hide_favorites: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's favorites timeline will be hidden"
|
||||
},
|
||||
show_role: %Schema{
|
||||
type: :boolean,
|
||||
description: "user's role (e.g admin, moderator) will be exposed to anyone in the
|
||||
API"
|
||||
},
|
||||
default_scope: VisibilityScope,
|
||||
pleroma_settings_store: %Schema{
|
||||
type: :object,
|
||||
description: "Opaque user settings to be saved on the backend."
|
||||
},
|
||||
skip_thread_containment: %Schema{
|
||||
type: :boolean,
|
||||
description: "Skip filtering out broken threads"
|
||||
},
|
||||
allow_following_move: %Schema{
|
||||
type: :boolean,
|
||||
description: "Allows automatically follow moved following accounts"
|
||||
},
|
||||
pleroma_background_image: %Schema{
|
||||
type: :string,
|
||||
description: "Sets the background image of the user.",
|
||||
format: :binary
|
||||
},
|
||||
discoverable: %Schema{
|
||||
type: :boolean,
|
||||
description:
|
||||
"Discovery of this account in search results and other services is allowed."
|
||||
},
|
||||
actor_type: ActorType
|
||||
},
|
||||
example: %{
|
||||
bot: false,
|
||||
display_name: "cofe",
|
||||
note: "foobar",
|
||||
fields_attributes: [%{name: "foo", value: "bar"}],
|
||||
no_rich_text: false,
|
||||
hide_followers: true,
|
||||
hide_follows: false,
|
||||
hide_followers_count: false,
|
||||
hide_follows_count: false,
|
||||
hide_favorites: false,
|
||||
show_role: false,
|
||||
default_scope: "private",
|
||||
pleroma_settings_store: %{"pleroma-fe" => %{"key" => "val"}},
|
||||
skip_thread_containment: false,
|
||||
allow_following_move: false,
|
||||
discoverable: false,
|
||||
actor_type: "Person"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_accounts do
|
||||
%Schema{
|
||||
title: "ArrayOfAccounts",
|
||||
type: :array,
|
||||
items: Account
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_relationships do
|
||||
%Schema{
|
||||
title: "ArrayOfRelationships",
|
||||
description: "Response schema for account relationships",
|
||||
type: :array,
|
||||
items: AccountRelationship,
|
||||
example: [
|
||||
%{
|
||||
"id" => "1",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => false,
|
||||
"blocked_by" => true,
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"endorsed" => true
|
||||
},
|
||||
%{
|
||||
"id" => "2",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => false,
|
||||
"blocked_by" => true,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"requested" => true,
|
||||
"domain_blocking" => false,
|
||||
"subscribing" => false,
|
||||
"endorsed" => false
|
||||
},
|
||||
%{
|
||||
"id" => "3",
|
||||
"following" => true,
|
||||
"showing_reblogs" => true,
|
||||
"followed_by" => true,
|
||||
"blocking" => true,
|
||||
"blocked_by" => false,
|
||||
"muting" => true,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"domain_blocking" => true,
|
||||
"subscribing" => true,
|
||||
"endorsed" => false
|
||||
}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp follow_by_uri_request do
|
||||
%Schema{
|
||||
title: "AccountFollowsRequest",
|
||||
description: "POST body for muting an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
uri: %Schema{type: :string, format: :uri}
|
||||
},
|
||||
required: [:uri]
|
||||
}
|
||||
end
|
||||
|
||||
defp mute_request do
|
||||
%Schema{
|
||||
title: "AccountMuteRequest",
|
||||
description: "POST body for muting an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
notifications: %Schema{
|
||||
type: :boolean,
|
||||
description: "Mute notifications in addition to statuses? Defaults to true.",
|
||||
default: true
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"notifications" => true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp list do
|
||||
%Schema{
|
||||
title: "List",
|
||||
description: "Response schema for a list",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
title: %Schema{type: :string}
|
||||
},
|
||||
example: %{
|
||||
"id" => "123",
|
||||
"title" => "my list"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_lists do
|
||||
%Schema{
|
||||
title: "ArrayOfLists",
|
||||
description: "Response schema for lists",
|
||||
type: :array,
|
||||
items: list(),
|
||||
example: [
|
||||
%{"id" => "123", "title" => "my list"},
|
||||
%{"id" => "1337", "title" => "anotehr list"}
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_statuses do
|
||||
%Schema{
|
||||
title: "ArrayOfStatuses",
|
||||
type: :array,
|
||||
items: Status
|
||||
}
|
||||
end
|
||||
|
||||
defp attribute_field do
|
||||
%Schema{
|
||||
title: "AccountAttributeField",
|
||||
description: "Request schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
value: %Schema{type: :string}
|
||||
},
|
||||
required: [:name, :value],
|
||||
example: %{
|
||||
"name" => "Website",
|
||||
"value" => "https://pleroma.com"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
|
@ -49,11 +49,7 @@ def verify_credentials_operation do
|
|||
summary: "Verify your app works",
|
||||
description: "Confirm that the app's OAuth2 credentials work.",
|
||||
operationId: "AppController.verify_credentials",
|
||||
security: [
|
||||
%{
|
||||
"oAuth" => ["read"]
|
||||
}
|
||||
],
|
||||
security: [%{"oAuth" => ["read"]}],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response("App", "application/json", %Schema{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
defmodule Pleroma.Web.ApiSpec.CustomEmojiOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.CustomEmoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
|
@ -19,17 +19,17 @@ def index_operation do
|
|||
description: "Returns custom emojis that are available on the server.",
|
||||
operationId: "CustomEmojiController.index",
|
||||
responses: %{
|
||||
200 => Operation.response("Custom Emojis", "application/json", custom_emojis_resposnse())
|
||||
200 => Operation.response("Custom Emojis", "application/json", resposnse())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp custom_emojis_resposnse do
|
||||
defp resposnse do
|
||||
%Schema{
|
||||
title: "CustomEmojisResponse",
|
||||
description: "Response schema for custom emojis",
|
||||
type: :array,
|
||||
items: CustomEmoji,
|
||||
items: custom_emoji(),
|
||||
example: [
|
||||
%{
|
||||
"category" => "Fun",
|
||||
|
@ -58,4 +58,31 @@ defp custom_emojis_resposnse do
|
|||
]
|
||||
}
|
||||
end
|
||||
|
||||
defp custom_emoji do
|
||||
%Schema{
|
||||
title: "CustomEmoji",
|
||||
description: "Schema for a CustomEmoji",
|
||||
allOf: [
|
||||
Emoji,
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
category: %Schema{type: :string},
|
||||
tags: %Schema{type: :array}
|
||||
}
|
||||
}
|
||||
],
|
||||
example: %{
|
||||
"category" => "Fun",
|
||||
"shortcode" => "aaaa",
|
||||
"url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png",
|
||||
"visible_in_picker" => true,
|
||||
"tags" => ["Gif", "Fun"]
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
231
lib/pleroma/web/api_spec/render_error.ex
Normal file
231
lib/pleroma/web/api_spec/render_error.ex
Normal file
|
@ -0,0 +1,231 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.RenderError do
|
||||
@behaviour Plug
|
||||
|
||||
import Plug.Conn, only: [put_status: 2]
|
||||
import Phoenix.Controller, only: [json: 2]
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
@impl Plug
|
||||
def init(opts), do: opts
|
||||
|
||||
@impl Plug
|
||||
|
||||
def call(conn, errors) do
|
||||
errors =
|
||||
Enum.map(errors, fn
|
||||
%{name: nil} = err ->
|
||||
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
|
||||
|
||||
err ->
|
||||
err
|
||||
end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json(%{
|
||||
error: errors |> Enum.map(&message/1) |> Enum.join(" "),
|
||||
errors: errors |> Enum.map(&render_error/1)
|
||||
})
|
||||
end
|
||||
|
||||
defp render_error(error) do
|
||||
pointer = OpenApiSpex.path_to_string(error)
|
||||
|
||||
%{
|
||||
title: "Invalid value",
|
||||
source: %{
|
||||
pointer: pointer
|
||||
},
|
||||
message: OpenApiSpex.Cast.Error.message(error)
|
||||
}
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_schema_type, type: type, name: name}) do
|
||||
gettext("%{name} - Invalid schema.type. Got: %{type}.",
|
||||
name: name,
|
||||
type: inspect(type)
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :null_value, name: name} = error) do
|
||||
case error.type do
|
||||
nil ->
|
||||
gettext("%{name} - null value.", name: name)
|
||||
|
||||
type ->
|
||||
gettext("%{name} - null value where %{type} expected.",
|
||||
name: name,
|
||||
type: type
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
|
||||
gettext(
|
||||
"Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
|
||||
invalid_schema: invalid_schema
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||
gettext("Failed to cast value using any of: %{failed_schemas}.",
|
||||
failed_schemas: failed_schemas
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||
gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_length, length: length, name: name}) do
|
||||
gettext("%{name} - String length is smaller than minLength: %{length}.",
|
||||
name: name,
|
||||
length: length
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_length, length: length, name: name}) do
|
||||
gettext("%{name} - String length is larger than maxLength: %{length}.",
|
||||
name: name,
|
||||
length: length
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unique_items, name: name}) do
|
||||
gettext("%{name} - Array items must be unique.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_items, length: min, value: array, name: name}) do
|
||||
gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
|
||||
name: name,
|
||||
length: length(array),
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_items, length: max, value: array, name: name}) do
|
||||
gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
|
||||
name: name,
|
||||
length: length(array),
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
|
||||
gettext("%{name} - %{count} is not a multiple of %{multiple}.",
|
||||
name: name,
|
||||
count: count,
|
||||
multiple: multiple
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :exclusive_max, length: max, value: value, name: name})
|
||||
when value >= max do
|
||||
gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
|
||||
name: name,
|
||||
value: value,
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :maximum, length: max, value: value, name: name})
|
||||
when value > max do
|
||||
gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
|
||||
name: name,
|
||||
value: value,
|
||||
max: max
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
|
||||
when value <= min do
|
||||
gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
|
||||
name: name,
|
||||
value: value,
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :minimum, length: min, value: value, name: name})
|
||||
when value < min do
|
||||
gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
|
||||
name: name,
|
||||
value: value,
|
||||
min: min
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do
|
||||
gettext("%{name} - Invalid %{type}. Got: %{value}.",
|
||||
name: name,
|
||||
value: OpenApiSpex.TermType.type(value),
|
||||
type: type
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_format, format: format, name: name}) do
|
||||
gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_enum, name: name}) do
|
||||
gettext("%{name} - Invalid value for enum.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
|
||||
gettext("Failed to cast to any schema in %{polymorphic_type}",
|
||||
polymorphic_type: polymorphic_type
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unexpected_field, name: name}) do
|
||||
gettext("Unexpected field: %{name}.", name: safe_string(name))
|
||||
end
|
||||
|
||||
defp message(%{reason: :no_value_for_discriminator, name: field}) do
|
||||
gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_discriminator_value, name: field}) do
|
||||
gettext("No value provided for required discriminator `%{field}`.", name: field)
|
||||
end
|
||||
|
||||
defp message(%{reason: :unknown_schema, name: name}) do
|
||||
gettext("Unknown schema: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :missing_field, name: name}) do
|
||||
gettext("Missing field: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :missing_header, name: name}) do
|
||||
gettext("Missing header: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :invalid_header, name: name}) do
|
||||
gettext("Invalid value for header: %{name}.", name: name)
|
||||
end
|
||||
|
||||
defp message(%{reason: :max_properties, meta: meta}) do
|
||||
gettext(
|
||||
"Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
|
||||
property_count: meta.property_count,
|
||||
max_properties: meta.max_properties
|
||||
)
|
||||
end
|
||||
|
||||
defp message(%{reason: :min_properties, meta: meta}) do
|
||||
gettext(
|
||||
"Object property count %{property_count} is less than minProperties: %{min_properties}",
|
||||
property_count: meta.property_count,
|
||||
min_properties: meta.min_properties
|
||||
)
|
||||
end
|
||||
|
||||
defp safe_string(string) do
|
||||
to_string(string) |> String.slice(0..39)
|
||||
end
|
||||
end
|
167
lib/pleroma/web/api_spec/schemas/account.ex
Normal file
167
lib/pleroma/web/api_spec/schemas/account.ex
Normal file
|
@ -0,0 +1,167 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.Account do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountField
|
||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ActorType
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Account",
|
||||
description: "Response schema for an account",
|
||||
type: :object,
|
||||
properties: %{
|
||||
acct: %Schema{type: :string},
|
||||
avatar_static: %Schema{type: :string, format: :uri},
|
||||
avatar: %Schema{type: :string, format: :uri},
|
||||
bot: %Schema{type: :boolean},
|
||||
created_at: %Schema{type: :string, format: "date-time"},
|
||||
display_name: %Schema{type: :string},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
fields: %Schema{type: :array, items: AccountField},
|
||||
follow_requests_count: %Schema{type: :integer},
|
||||
followers_count: %Schema{type: :integer},
|
||||
following_count: %Schema{type: :integer},
|
||||
header_static: %Schema{type: :string, format: :uri},
|
||||
header: %Schema{type: :string, format: :uri},
|
||||
id: FlakeID,
|
||||
locked: %Schema{type: :boolean},
|
||||
note: %Schema{type: :string, format: :html},
|
||||
statuses_count: %Schema{type: :integer},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
username: %Schema{type: :string},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
allow_following_move: %Schema{type: :boolean},
|
||||
background_image: %Schema{type: :string, nullable: true},
|
||||
chat_token: %Schema{type: :string},
|
||||
confirmation_pending: %Schema{type: :boolean},
|
||||
hide_favorites: %Schema{type: :boolean},
|
||||
hide_followers_count: %Schema{type: :boolean},
|
||||
hide_followers: %Schema{type: :boolean},
|
||||
hide_follows_count: %Schema{type: :boolean},
|
||||
hide_follows: %Schema{type: :boolean},
|
||||
is_admin: %Schema{type: :boolean},
|
||||
is_moderator: %Schema{type: :boolean},
|
||||
skip_thread_containment: %Schema{type: :boolean},
|
||||
tags: %Schema{type: :array, items: %Schema{type: :string}},
|
||||
unread_conversation_count: %Schema{type: :integer},
|
||||
notification_settings: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
followers: %Schema{type: :boolean},
|
||||
follows: %Schema{type: :boolean},
|
||||
non_followers: %Schema{type: :boolean},
|
||||
non_follows: %Schema{type: :boolean},
|
||||
privacy_option: %Schema{type: :boolean}
|
||||
}
|
||||
},
|
||||
relationship: AccountRelationship,
|
||||
settings_store: %Schema{
|
||||
type: :object
|
||||
}
|
||||
}
|
||||
},
|
||||
source: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
fields: %Schema{type: :array, items: AccountField},
|
||||
note: %Schema{type: :string},
|
||||
privacy: VisibilityScope,
|
||||
sensitive: %Schema{type: :boolean},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
actor_type: ActorType,
|
||||
discoverable: %Schema{type: :boolean},
|
||||
no_rich_text: %Schema{type: :boolean},
|
||||
show_role: %Schema{type: :boolean}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
example: %{
|
||||
"acct" => "foobar",
|
||||
"avatar" => "https://mypleroma.com/images/avi.png",
|
||||
"avatar_static" => "https://mypleroma.com/images/avi.png",
|
||||
"bot" => false,
|
||||
"created_at" => "2020-03-24T13:05:58.000Z",
|
||||
"display_name" => "foobar",
|
||||
"emojis" => [],
|
||||
"fields" => [],
|
||||
"follow_requests_count" => 0,
|
||||
"followers_count" => 0,
|
||||
"following_count" => 1,
|
||||
"header" => "https://mypleroma.com/images/banner.png",
|
||||
"header_static" => "https://mypleroma.com/images/banner.png",
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"locked" => false,
|
||||
"note" => "cofe",
|
||||
"pleroma" => %{
|
||||
"allow_following_move" => true,
|
||||
"background_image" => nil,
|
||||
"confirmation_pending" => true,
|
||||
"hide_favorites" => true,
|
||||
"hide_followers" => false,
|
||||
"hide_followers_count" => false,
|
||||
"hide_follows" => false,
|
||||
"hide_follows_count" => false,
|
||||
"is_admin" => false,
|
||||
"is_moderator" => false,
|
||||
"skip_thread_containment" => false,
|
||||
"chat_token" =>
|
||||
"SFMyNTY.g3QAAAACZAAEZGF0YW0AAAASOXRLaTNlc2JHN09RZ1oyOTIwZAAGc2lnbmVkbgYARNplS3EB.Mb_Iaqew2bN1I1o79B_iP7encmVCpTKC4OtHZRxdjKc",
|
||||
"unread_conversation_count" => 0,
|
||||
"tags" => [],
|
||||
"notification_settings" => %{
|
||||
"followers" => true,
|
||||
"follows" => true,
|
||||
"non_followers" => true,
|
||||
"non_follows" => true,
|
||||
"privacy_option" => false
|
||||
},
|
||||
"relationship" => %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => false,
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
},
|
||||
"settings_store" => %{
|
||||
"pleroma-fe" => %{}
|
||||
}
|
||||
},
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
"note" => "foobar",
|
||||
"pleroma" => %{
|
||||
"actor_type" => "Person",
|
||||
"discoverable" => false,
|
||||
"no_rich_text" => false,
|
||||
"show_role" => true
|
||||
},
|
||||
"privacy" => "public",
|
||||
"sensitive" => false
|
||||
},
|
||||
"statuses_count" => 0,
|
||||
"url" => "https://mypleroma.com/users/foobar",
|
||||
"username" => "foobar"
|
||||
}
|
||||
})
|
||||
end
|
26
lib/pleroma/web/api_spec/schemas/account_field.ex
Normal file
26
lib/pleroma/web/api_spec/schemas/account_field.ex
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.AccountField do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AccountField",
|
||||
description: "Response schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
value: %Schema{type: :string, format: :html},
|
||||
verified_at: %Schema{type: :string, format: :"date-time", nullable: true}
|
||||
},
|
||||
example: %{
|
||||
"name" => "Website",
|
||||
"value" =>
|
||||
"<a href=\"https://pleroma.com\" rel=\"me nofollow noopener noreferrer\" target=\"_blank\"><span class=\"invisible\">https://</span><span class=\"\">pleroma.com</span><span class=\"invisible\"></span></a>",
|
||||
"verified_at" => "2019-08-29T04:14:55.571+00:00"
|
||||
}
|
||||
})
|
||||
end
|
44
lib/pleroma/web/api_spec/schemas/account_relationship.ex
Normal file
44
lib/pleroma/web/api_spec/schemas/account_relationship.ex
Normal file
|
@ -0,0 +1,44 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "AccountRelationship",
|
||||
description: "Response schema for relationship",
|
||||
type: :object,
|
||||
properties: %{
|
||||
blocked_by: %Schema{type: :boolean},
|
||||
blocking: %Schema{type: :boolean},
|
||||
domain_blocking: %Schema{type: :boolean},
|
||||
endorsed: %Schema{type: :boolean},
|
||||
followed_by: %Schema{type: :boolean},
|
||||
following: %Schema{type: :boolean},
|
||||
id: FlakeID,
|
||||
muting: %Schema{type: :boolean},
|
||||
muting_notifications: %Schema{type: :boolean},
|
||||
requested: %Schema{type: :boolean},
|
||||
showing_reblogs: %Schema{type: :boolean},
|
||||
subscribing: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => false,
|
||||
"id" => "9tKi3esbG7OQgZ2920",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
}
|
||||
})
|
||||
end
|
13
lib/pleroma/web/api_spec/schemas/actor_type.ex
Normal file
13
lib/pleroma/web/api_spec/schemas/actor_type.ex
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.ActorType do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ActorType",
|
||||
type: :string,
|
||||
enum: ["Application", "Group", "Organization", "Person", "Service"]
|
||||
})
|
||||
end
|
19
lib/pleroma/web/api_spec/schemas/api_error.ex
Normal file
19
lib/pleroma/web/api_spec/schemas/api_error.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.ApiError do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "ApiError",
|
||||
description: "Response schema for API error",
|
||||
type: :object,
|
||||
properties: %{error: %Schema{type: :string}},
|
||||
example: %{
|
||||
"error" => "Something went wrong"
|
||||
}
|
||||
})
|
||||
end
|
36
lib/pleroma/web/api_spec/schemas/boolean_like.ex
Normal file
36
lib/pleroma/web/api_spec/schemas/boolean_like.ex
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.BooleanLike do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "BooleanLike",
|
||||
description: """
|
||||
The following values will be treated as `false`:
|
||||
- false
|
||||
- 0
|
||||
- "0",
|
||||
- "f",
|
||||
- "F",
|
||||
- "false",
|
||||
- "FALSE",
|
||||
- "off",
|
||||
- "OFF"
|
||||
|
||||
All other non-null values will be treated as `true`
|
||||
""",
|
||||
anyOf: [
|
||||
%Schema{type: :boolean},
|
||||
%Schema{type: :string},
|
||||
%Schema{type: :integer}
|
||||
]
|
||||
})
|
||||
|
||||
def after_cast(value, _schmea) do
|
||||
{:ok, Pleroma.Web.ControllerHelper.truthy_param?(value)}
|
||||
end
|
||||
end
|
|
@ -1,30 +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.Web.ApiSpec.Schemas.CustomEmoji do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "CustomEmoji",
|
||||
description: "Response schema for an CustomEmoji",
|
||||
type: :object,
|
||||
properties: %{
|
||||
shortcode: %Schema{type: :string},
|
||||
url: %Schema{type: :string},
|
||||
static_url: %Schema{type: :string},
|
||||
visible_in_picker: %Schema{type: :boolean},
|
||||
category: %Schema{type: :string},
|
||||
tags: %Schema{type: :array}
|
||||
},
|
||||
example: %{
|
||||
"shortcode" => "aaaa",
|
||||
"url" => "https://files.mastodon.social/custom_emojis/images/000/007/118/original/aaaa.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/007/118/static/aaaa.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
})
|
||||
end
|
29
lib/pleroma/web/api_spec/schemas/emoji.ex
Normal file
29
lib/pleroma/web/api_spec/schemas/emoji.ex
Normal file
|
@ -0,0 +1,29 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.Emoji do
|
||||
alias OpenApiSpex.Schema
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Emoji",
|
||||
description: "Response schema for an emoji",
|
||||
type: :object,
|
||||
properties: %{
|
||||
shortcode: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
static_url: %Schema{type: :string, format: :uri},
|
||||
visible_in_picker: %Schema{type: :boolean}
|
||||
},
|
||||
example: %{
|
||||
"shortcode" => "fatyoshi",
|
||||
"url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/023/920/original/e57ecb623faa0dc9.png",
|
||||
"static_url" =>
|
||||
"https://files.mastodon.social/custom_emojis/images/000/023/920/static/e57ecb623faa0dc9.png",
|
||||
"visible_in_picker" => true
|
||||
}
|
||||
})
|
||||
end
|
14
lib/pleroma/web/api_spec/schemas/flake_id.ex
Normal file
14
lib/pleroma/web/api_spec/schemas/flake_id.ex
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.FlakeID do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "FlakeID",
|
||||
description:
|
||||
"Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings",
|
||||
type: :string
|
||||
})
|
||||
end
|
36
lib/pleroma/web/api_spec/schemas/poll.ex
Normal file
36
lib/pleroma/web/api_spec/schemas/poll.ex
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Poll",
|
||||
description: "Response schema for account custom fields",
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: FlakeID,
|
||||
expires_at: %Schema{type: :string, format: "date-time"},
|
||||
expired: %Schema{type: :boolean},
|
||||
multiple: %Schema{type: :boolean},
|
||||
votes_count: %Schema{type: :integer},
|
||||
voted: %Schema{type: :boolean},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
options: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
title: %Schema{type: :string},
|
||||
votes_count: %Schema{type: :integer}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
226
lib/pleroma/web/api_spec/schemas/status.ex
Normal file
226
lib/pleroma/web/api_spec/schemas/status.ex
Normal file
|
@ -0,0 +1,226 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Account
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Emoji
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.Schemas.Poll
|
||||
alias Pleroma.Web.ApiSpec.Schemas.VisibilityScope
|
||||
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "Status",
|
||||
description: "Response schema for a status",
|
||||
type: :object,
|
||||
properties: %{
|
||||
account: Account,
|
||||
application: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
website: %Schema{type: :string, nullable: true, format: :uri}
|
||||
}
|
||||
},
|
||||
bookmarked: %Schema{type: :boolean},
|
||||
card: %Schema{
|
||||
type: :object,
|
||||
nullable: true,
|
||||
properties: %{
|
||||
type: %Schema{type: :string, enum: ["link", "photo", "video", "rich"]},
|
||||
provider_name: %Schema{type: :string, nullable: true},
|
||||
provider_url: %Schema{type: :string, format: :uri},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
image: %Schema{type: :string, nullable: true, format: :uri},
|
||||
title: %Schema{type: :string},
|
||||
description: %Schema{type: :string}
|
||||
}
|
||||
},
|
||||
content: %Schema{type: :string, format: :html},
|
||||
created_at: %Schema{type: :string, format: "date-time"},
|
||||
emojis: %Schema{type: :array, items: Emoji},
|
||||
favourited: %Schema{type: :boolean},
|
||||
favourites_count: %Schema{type: :integer},
|
||||
id: FlakeID,
|
||||
in_reply_to_account_id: %Schema{type: :string, nullable: true},
|
||||
in_reply_to_id: %Schema{type: :string, nullable: true},
|
||||
language: %Schema{type: :string, nullable: true},
|
||||
media_attachments: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri},
|
||||
remote_url: %Schema{type: :string, format: :uri},
|
||||
preview_url: %Schema{type: :string, format: :uri},
|
||||
text_url: %Schema{type: :string, format: :uri},
|
||||
description: %Schema{type: :string},
|
||||
type: %Schema{type: :string, enum: ["image", "video", "audio", "unknown"]},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{mime_type: %Schema{type: :string}}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mentions: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
acct: %Schema{type: :string},
|
||||
username: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri}
|
||||
}
|
||||
}
|
||||
},
|
||||
muted: %Schema{type: :boolean},
|
||||
pinned: %Schema{type: :boolean},
|
||||
pleroma: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
content: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
|
||||
conversation_id: %Schema{type: :integer},
|
||||
direct_conversation_id: %Schema{type: :string, nullable: true},
|
||||
emoji_reactions: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
count: %Schema{type: :integer},
|
||||
me: %Schema{type: :boolean}
|
||||
}
|
||||
}
|
||||
},
|
||||
expires_at: %Schema{type: :string, format: "date-time", nullable: true},
|
||||
in_reply_to_account_acct: %Schema{type: :string, nullable: true},
|
||||
local: %Schema{type: :boolean},
|
||||
spoiler_text: %Schema{type: :object, additionalProperties: %Schema{type: :string}},
|
||||
thread_muted: %Schema{type: :boolean}
|
||||
}
|
||||
},
|
||||
poll: %Schema{type: Poll, nullable: true},
|
||||
reblog: %Schema{
|
||||
allOf: [%OpenApiSpex.Reference{"$ref": "#/components/schemas/Status"}],
|
||||
nullable: true
|
||||
},
|
||||
reblogged: %Schema{type: :boolean},
|
||||
reblogs_count: %Schema{type: :integer},
|
||||
replies_count: %Schema{type: :integer},
|
||||
sensitive: %Schema{type: :boolean},
|
||||
spoiler_text: %Schema{type: :string},
|
||||
tags: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
name: %Schema{type: :string},
|
||||
url: %Schema{type: :string, format: :uri}
|
||||
}
|
||||
}
|
||||
},
|
||||
uri: %Schema{type: :string, format: :uri},
|
||||
url: %Schema{type: :string, nullable: true, format: :uri},
|
||||
visibility: VisibilityScope
|
||||
},
|
||||
example: %{
|
||||
"account" => %{
|
||||
"acct" => "nick6",
|
||||
"avatar" => "http://localhost:4001/images/avi.png",
|
||||
"avatar_static" => "http://localhost:4001/images/avi.png",
|
||||
"bot" => false,
|
||||
"created_at" => "2020-04-07T19:48:51.000Z",
|
||||
"display_name" => "Test テスト User 6",
|
||||
"emojis" => [],
|
||||
"fields" => [],
|
||||
"followers_count" => 1,
|
||||
"following_count" => 0,
|
||||
"header" => "http://localhost:4001/images/banner.png",
|
||||
"header_static" => "http://localhost:4001/images/banner.png",
|
||||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"locked" => false,
|
||||
"note" => "Tester Number 6",
|
||||
"pleroma" => %{
|
||||
"background_image" => nil,
|
||||
"confirmation_pending" => false,
|
||||
"hide_favorites" => true,
|
||||
"hide_followers" => false,
|
||||
"hide_followers_count" => false,
|
||||
"hide_follows" => false,
|
||||
"hide_follows_count" => false,
|
||||
"is_admin" => false,
|
||||
"is_moderator" => false,
|
||||
"relationship" => %{
|
||||
"blocked_by" => false,
|
||||
"blocking" => false,
|
||||
"domain_blocking" => false,
|
||||
"endorsed" => false,
|
||||
"followed_by" => false,
|
||||
"following" => true,
|
||||
"id" => "9toJCsKN7SmSf3aj5c",
|
||||
"muting" => false,
|
||||
"muting_notifications" => false,
|
||||
"requested" => false,
|
||||
"showing_reblogs" => true,
|
||||
"subscribing" => false
|
||||
},
|
||||
"skip_thread_containment" => false,
|
||||
"tags" => []
|
||||
},
|
||||
"source" => %{
|
||||
"fields" => [],
|
||||
"note" => "Tester Number 6",
|
||||
"pleroma" => %{"actor_type" => "Person", "discoverable" => false},
|
||||
"sensitive" => false
|
||||
},
|
||||
"statuses_count" => 1,
|
||||
"url" => "http://localhost:4001/users/nick6",
|
||||
"username" => "nick6"
|
||||
},
|
||||
"application" => %{"name" => "Web", "website" => nil},
|
||||
"bookmarked" => false,
|
||||
"card" => nil,
|
||||
"content" => "foobar",
|
||||
"created_at" => "2020-04-07T19:48:51.000Z",
|
||||
"emojis" => [],
|
||||
"favourited" => false,
|
||||
"favourites_count" => 0,
|
||||
"id" => "9toJCu5YZW7O7gfvH6",
|
||||
"in_reply_to_account_id" => nil,
|
||||
"in_reply_to_id" => nil,
|
||||
"language" => nil,
|
||||
"media_attachments" => [],
|
||||
"mentions" => [],
|
||||
"muted" => false,
|
||||
"pinned" => false,
|
||||
"pleroma" => %{
|
||||
"content" => %{"text/plain" => "foobar"},
|
||||
"conversation_id" => 345_972,
|
||||
"direct_conversation_id" => nil,
|
||||
"emoji_reactions" => [],
|
||||
"expires_at" => nil,
|
||||
"in_reply_to_account_acct" => nil,
|
||||
"local" => true,
|
||||
"spoiler_text" => %{"text/plain" => ""},
|
||||
"thread_muted" => false
|
||||
},
|
||||
"poll" => nil,
|
||||
"reblog" => nil,
|
||||
"reblogged" => false,
|
||||
"reblogs_count" => 0,
|
||||
"replies_count" => 0,
|
||||
"sensitive" => false,
|
||||
"spoiler_text" => "",
|
||||
"tags" => [],
|
||||
"uri" => "http://localhost:4001/objects/0f5dad44-0e9e-4610-b377-a2631e499190",
|
||||
"url" => "http://localhost:4001/notice/9toJCu5YZW7O7gfvH6",
|
||||
"visibility" => "private"
|
||||
}
|
||||
})
|
||||
end
|
14
lib/pleroma/web/api_spec/schemas/visibility_scope.ex
Normal file
14
lib/pleroma/web/api_spec/schemas/visibility_scope.ex
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.Schemas.VisibilityScope do
|
||||
require OpenApiSpex
|
||||
|
||||
OpenApiSpex.schema(%{
|
||||
title: "VisibilityScope",
|
||||
description: "Status visibility",
|
||||
type: :string,
|
||||
enum: ["public", "unlisted", "private", "direct"]
|
||||
})
|
||||
end
|
|
@ -82,8 +82,9 @@ def add_link_headers(conn, activities, extra_params) do
|
|||
end
|
||||
end
|
||||
|
||||
def assign_account_by_id(%{params: %{"id" => id}} = conn, _) do
|
||||
case Pleroma.User.get_cached_by_id(id) do
|
||||
def assign_account_by_id(conn, _) do
|
||||
# TODO: use `conn.params[:id]` only after moving to OpenAPI
|
||||
case Pleroma.User.get_cached_by_id(conn.params[:id] || conn.params["id"]) do
|
||||
%Pleroma.User{} = account -> assign(conn, :account, account)
|
||||
nil -> Pleroma.Web.MastodonAPI.FallbackController.call(conn, {:error, :not_found}) |> halt()
|
||||
end
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
|
||||
defmodule Fallback.RedirectController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Metadata
|
||||
|
||||
|
|
|
@ -5,19 +5,25 @@
|
|||
defmodule Pleroma.Web.MastoFEController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:accounts"]} when action == :put_settings)
|
||||
|
||||
# Note: :index action handles attempt of unauthenticated access to private instance with redirect
|
||||
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action == :index)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"], fallback: :proceed_unauthenticated, skip_instance_privacy_check: true}
|
||||
%{scopes: ["read"], fallback: :proceed_unauthenticated}
|
||||
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"
|
||||
def index(%{assigns: %{user: user, token: token}} = conn, _params)
|
||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
skip_relationships?: 1
|
||||
]
|
||||
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
|
@ -26,18 +27,28 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
|
||||
plug(:skip_plug, OAuthScopesPlug when action == :identity_proofs)
|
||||
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||
|
||||
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||
|
||||
plug(:skip_plug, EnsurePublicOrAuthenticatedPlug when action in [:show, :statuses])
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{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(
|
||||
OAuthScopesPlug,
|
||||
%{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)
|
||||
|
@ -56,21 +67,15 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:follows"]} when action == :relationships)
|
||||
|
||||
# Note: :follows (POST /api/v1/follows) is the same as :follow, consider removing :follows
|
||||
plug(
|
||||
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", "write:mutes"]} when action in [:mute, :unmute])
|
||||
|
||||
plug(
|
||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
when action not in [:create, :show, :statuses]
|
||||
)
|
||||
|
||||
@relationship_actions [:follow, :unfollow]
|
||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock)a
|
||||
|
||||
|
@ -85,26 +90,26 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AccountOperation
|
||||
|
||||
@doc "POST /api/v1/accounts"
|
||||
def create(
|
||||
%{assigns: %{app: app}} = conn,
|
||||
%{"username" => nickname, "password" => _, "agreement" => true} = params
|
||||
) do
|
||||
def create(%{assigns: %{app: app}, body_params: params} = conn, _params) do
|
||||
params =
|
||||
params
|
||||
|> Map.take([
|
||||
"email",
|
||||
"captcha_solution",
|
||||
"captcha_token",
|
||||
"captcha_answer_data",
|
||||
"token",
|
||||
"password"
|
||||
:email,
|
||||
:bio,
|
||||
:captcha_solution,
|
||||
:captcha_token,
|
||||
:captcha_answer_data,
|
||||
:token,
|
||||
:password,
|
||||
:fullname
|
||||
])
|
||||
|> Map.put("nickname", nickname)
|
||||
|> Map.put("fullname", params["fullname"] || nickname)
|
||||
|> Map.put("bio", params["bio"] || "")
|
||||
|> Map.put("confirm", params["password"])
|
||||
|> Map.put("trusted_app", app.trusted)
|
||||
|> Map.put(:nickname, params.username)
|
||||
|> Map.put(:fullname, Map.get(params, :fullname, params.username))
|
||||
|> Map.put(:confirm, params.password)
|
||||
|> Map.put(:trusted_app, app.trusted)
|
||||
|
||||
with :ok <- validate_email_param(params),
|
||||
{:ok, user} <- TwitterAPI.register_user(params, need_confirmation: true),
|
||||
|
@ -128,7 +133,7 @@ def create(conn, _) do
|
|||
render_error(conn, :forbidden, "Invalid credentials")
|
||||
end
|
||||
|
||||
defp validate_email_param(%{"email" => _}), do: :ok
|
||||
defp validate_email_param(%{:email => email}) when not is_nil(email), do: :ok
|
||||
|
||||
defp validate_email_param(_) do
|
||||
case Pleroma.Config.get([:instance, :account_activation_required]) do
|
||||
|
@ -150,7 +155,14 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
|||
end
|
||||
|
||||
@doc "PATCH /api/v1/accounts/update_credentials"
|
||||
def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||
def update_credentials(%{assigns: %{user: original_user}, body_params: params} = conn, _params) do
|
||||
user = original_user
|
||||
|
||||
params =
|
||||
params
|
||||
|> Enum.filter(fn {_, value} -> not is_nil(value) end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
user_params =
|
||||
[
|
||||
:no_rich_text,
|
||||
|
@ -166,22 +178,22 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
|||
:discoverable
|
||||
]
|
||||
|> Enum.reduce(%{}, fn key, acc ->
|
||||
add_if_present(acc, params, to_string(key), key, &{:ok, truthy_param?(&1)})
|
||||
add_if_present(acc, params, key, key, &{:ok, truthy_param?(&1)})
|
||||
end)
|
||||
|> add_if_present(params, "display_name", :name)
|
||||
|> add_if_present(params, "note", :bio)
|
||||
|> add_if_present(params, "avatar", :avatar)
|
||||
|> add_if_present(params, "header", :banner)
|
||||
|> add_if_present(params, "pleroma_background_image", :background)
|
||||
|> add_if_present(params, :display_name, :name)
|
||||
|> add_if_present(params, :note, :bio)
|
||||
|> add_if_present(params, :avatar, :avatar)
|
||||
|> add_if_present(params, :header, :banner)
|
||||
|> add_if_present(params, :pleroma_background_image, :background)
|
||||
|> add_if_present(
|
||||
params,
|
||||
"fields_attributes",
|
||||
:fields_attributes,
|
||||
:raw_fields,
|
||||
&{:ok, normalize_fields_attributes(&1)}
|
||||
)
|
||||
|> add_if_present(params, "pleroma_settings_store", :pleroma_settings_store)
|
||||
|> add_if_present(params, "default_scope", :default_scope)
|
||||
|> add_if_present(params, "actor_type", :actor_type)
|
||||
|> add_if_present(params, :pleroma_settings_store, :pleroma_settings_store)
|
||||
|> add_if_present(params, :default_scope, :default_scope)
|
||||
|> add_if_present(params, :actor_type, :actor_type)
|
||||
|
||||
changeset = User.update_changeset(user, user_params)
|
||||
|
||||
|
@ -194,7 +206,7 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
|||
|
||||
defp add_if_present(map, params, params_field, map_field, value_function \\ &{:ok, &1}) do
|
||||
with true <- Map.has_key?(params, params_field),
|
||||
{:ok, new_value} <- value_function.(params[params_field]) do
|
||||
{:ok, new_value} <- value_function.(Map.get(params, params_field)) do
|
||||
Map.put(map, map_field, new_value)
|
||||
else
|
||||
_ -> map
|
||||
|
@ -205,12 +217,15 @@ defp normalize_fields_attributes(fields) do
|
|||
if Enum.all?(fields, &is_tuple/1) do
|
||||
Enum.map(fields, fn {_, v} -> v end)
|
||||
else
|
||||
fields
|
||||
Enum.map(fields, fn
|
||||
%{} = field -> %{"name" => field.name, "value" => field.value}
|
||||
field -> field
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/accounts/relationships"
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
def relationships(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||
targets = User.get_all_by_ids(List.wrap(id))
|
||||
|
||||
render(conn, "relationships.json", user: user, targets: targets)
|
||||
|
@ -220,7 +235,7 @@ def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|||
def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
||||
|
||||
@doc "GET /api/v1/accounts/:id"
|
||||
def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
||||
def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
||||
true <- User.visible_for?(user, for_user) do
|
||||
render(conn, "show.json", user: user, for: for_user)
|
||||
|
@ -231,12 +246,14 @@ def show(%{assigns: %{user: for_user}} = conn, %{"id" => nickname_or_id}) do
|
|||
|
||||
@doc "GET /api/v1/accounts/:id/statuses"
|
||||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params["id"], for: reading_user),
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
|
||||
true <- User.visible_for?(user, reading_user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("tag", params["tagged"])
|
||||
|> Map.delete("godmode")
|
||||
|> Map.delete(:tagged)
|
||||
|> Enum.filter(&(not is_nil(&1)))
|
||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Map.put("tag", params[:tagged])
|
||||
|
||||
activities = ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||
|
||||
|
@ -256,6 +273,11 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
|||
|
||||
@doc "GET /api/v1/accounts/:id/followers"
|
||||
def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> MastodonAPI.get_followers(user, params)
|
||||
|
@ -270,6 +292,11 @@ def followers(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
|||
|
||||
@doc "GET /api/v1/accounts/:id/following"
|
||||
def following(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||
params =
|
||||
params
|
||||
|> Enum.map(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Enum.into(%{})
|
||||
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> MastodonAPI.get_friends(user, params)
|
||||
|
@ -296,8 +323,8 @@ def follow(%{assigns: %{user: %{id: id}, account: %{id: id}}}, _params) do
|
|||
{:error, "Can not follow yourself"}
|
||||
end
|
||||
|
||||
def follow(%{assigns: %{user: follower, account: followed}} = conn, _params) do
|
||||
with {:ok, follower} <- MastodonAPI.follow(follower, followed, conn.params) do
|
||||
def follow(%{assigns: %{user: follower, account: followed}} = conn, params) do
|
||||
with {:ok, follower} <- MastodonAPI.follow(follower, followed, params) do
|
||||
render(conn, "relationship.json", user: follower, target: followed)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
@ -316,10 +343,8 @@ def unfollow(%{assigns: %{user: follower, account: followed}} = conn, _params) d
|
|||
end
|
||||
|
||||
@doc "POST /api/v1/accounts/:id/mute"
|
||||
def mute(%{assigns: %{user: muter, account: muted}} = conn, params) do
|
||||
notifications? = params |> Map.get("notifications", true) |> truthy_param?()
|
||||
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, notifications?) do
|
||||
def mute(%{assigns: %{user: muter, account: muted}, body_params: params} = conn, _params) do
|
||||
with {:ok, _user_relationships} <- User.mute(muter, muted, params.notifications) do
|
||||
render(conn, "relationship.json", user: muter, target: muted)
|
||||
else
|
||||
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||
|
@ -356,7 +381,7 @@ def unblock(%{assigns: %{user: blocker, account: blocked}} = conn, _params) do
|
|||
end
|
||||
|
||||
@doc "POST /api/v1/follows"
|
||||
def follows(conn, %{"uri" => uri}) do
|
||||
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||
case User.get_cached_by_nickname(uri) do
|
||||
%User{} = user ->
|
||||
conn
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.OAuth.App
|
||||
|
@ -13,7 +14,14 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
|||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug]
|
||||
when action == :create
|
||||
)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :verify_credentials)
|
||||
|
||||
plug(OpenApiSpex.Plug.CastAndValidate)
|
||||
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
|
|
@ -13,10 +13,10 @@ defmodule Pleroma.Web.MastodonAPI.AuthController do
|
|||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
plug(Pleroma.Plugs.RateLimiter, [name: :password_reset] when action == :password_reset)
|
||||
|
||||
@local_mastodon_name "Mastodon-Local"
|
||||
|
||||
@doc "GET /web/login"
|
||||
def login(%{assigns: %{user: %User{}}} = conn, _params) do
|
||||
redirect(conn, to: local_mastodon_root_path(conn))
|
||||
|
|
|
@ -14,9 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationController do
|
|||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action == :index)
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action == :read)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:conversations"]} when action != :index)
|
||||
|
||||
@doc "GET /api/v1/conversations"
|
||||
def index(%{assigns: %{user: user}} = conn, params) do
|
||||
|
@ -28,7 +26,7 @@ def index(%{assigns: %{user: user}} = conn, params) do
|
|||
end
|
||||
|
||||
@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 <-
|
||||
Repo.get_by(Participation, id: participation_id, user_id: user.id),
|
||||
{:ok, participation} <- Participation.mark_as_read(participation) do
|
||||
|
|
|
@ -7,6 +7,12 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiController do
|
|||
|
||||
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
|
||||
|
||||
def index(conn, _params) do
|
||||
|
|
|
@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.DomainBlockController do
|
|||
%{scopes: ["follow", "write:blocks"]} when action != :index
|
||||
)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "GET /api/v1/domain_blocks"
|
||||
def index(%{assigns: %{user: user}} = conn, _) do
|
||||
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
|
||||
)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "GET /api/v1/filters"
|
||||
def index(%{assigns: %{user: user}} = conn, _) do
|
||||
filters = Filter.get_filters(user)
|
||||
|
|
|
@ -21,8 +21,6 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
|||
%{scopes: ["follow", "write:follows"]} when action != :index
|
||||
)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "GET /api/v1/follow_requests"
|
||||
def index(%{assigns: %{user: followed}} = conn, _params) do
|
||||
follow_requests = User.get_follow_requests(followed)
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||
when action in [:show, :peers]
|
||||
)
|
||||
|
||||
@doc "GET /api/v1/instance"
|
||||
def show(conn, _params) do
|
||||
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(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(
|
||||
OAuthScopesPlug,
|
||||
%{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)
|
||||
|
||||
# GET /api/v1/lists
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.MastodonAPI.MarkerController do
|
|||
)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:statuses"]} when action == :upsert)
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
# GET /api/v1/markers
|
||||
|
|
|
@ -15,9 +15,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
|
||||
require Logger
|
||||
|
||||
plug(:skip_plug, Pleroma.Plugs.OAuthScopesPlug when action in [:empty_array, :empty_object])
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
plug(
|
||||
:skip_plug,
|
||||
[Pleroma.Plugs.OAuthScopesPlug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug]
|
||||
when action in [:empty_array, :empty_object]
|
||||
)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
|||
|
||||
plug(OAuthScopesPlug, %{scopes: ["write:media"]})
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "POST /api/v1/media"
|
||||
def create(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||
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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
# GET /api/v1/notifications
|
||||
def index(conn, %{"account_id" => account_id} = params) 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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||
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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "POST /api/v1/reports"
|
||||
def create(%{assigns: %{user: user}} = conn, 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: ["write:statuses"]} when action not in @oauth_read_actions)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
@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)
|
||||
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])
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
alias Pleroma.Web.MastodonAPI.AccountView
|
||||
alias Pleroma.Web.MastodonAPI.ScheduledActivityView
|
||||
|
||||
plug(:skip_plug, Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug when action in [:index, :show])
|
||||
|
||||
@unauthenticated_access %{fallback: :proceed_unauthenticated, scopes: []}
|
||||
|
||||
plug(
|
||||
|
@ -77,8 +79,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do
|
|||
%{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
|
||||
|
||||
plug(
|
||||
|
@ -358,7 +358,7 @@ def context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|||
end
|
||||
|
||||
@doc "GET /api/v1/favourites"
|
||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||
def favourites(%{assigns: %{user: %User{} = user}} = conn, params) do
|
||||
activities =
|
||||
ActivityPub.fetch_favourites(
|
||||
user,
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
|||
action_fallback(:errors)
|
||||
|
||||
plug(Pleroma.Plugs.OAuthScopesPlug, %{scopes: ["push"]})
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
plug(:restrict_push_enabled)
|
||||
|
||||
# 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]
|
||||
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
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:
|
||||
# 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: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)
|
||||
|
||||
|
@ -94,7 +101,9 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
|||
|
||||
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 =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|
@ -112,12 +121,10 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
|||
as: :activity,
|
||||
skip_relationships: skip_relationships?(params)
|
||||
)
|
||||
else
|
||||
render_error(conn, :unauthorized, "authorization required for timeline view")
|
||||
end
|
||||
end
|
||||
|
||||
def hashtag_fetching(params, user, local_only) do
|
||||
defp hashtag_fetching(params, user, local_only) do
|
||||
tags =
|
||||
[params["tag"], params["any"]]
|
||||
|> List.flatten()
|
||||
|
|
|
@ -529,11 +529,9 @@ def render_content(object), do: object.data["content"] || ""
|
|||
"""
|
||||
@spec build_tags(list(any())) :: list(map())
|
||||
def build_tags(object_tags) when is_list(object_tags) do
|
||||
object_tags = for tag when is_binary(tag) <- object_tags, do: tag
|
||||
|
||||
Enum.reduce(object_tags, [], fn tag, tags ->
|
||||
tags ++ [%{name: tag, url: "/tag/#{URI.encode(tag)}"}]
|
||||
end)
|
||||
object_tags
|
||||
|> Enum.filter(&is_binary/1)
|
||||
|> Enum.map(&%{name: &1, url: "/tag/#{URI.encode(&1)}"})
|
||||
end
|
||||
|
||||
def build_tags(_), do: []
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.ReverseProxy
|
||||
alias Pleroma.Web.MediaProxy
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ defmodule Pleroma.Web.MongooseIM.MongooseIMController do
|
|||
plug(RateLimiter, [name: :authentication, params: ["user"]] when action == :check_password)
|
||||
|
||||
def user_exists(conn, %{"user" => username}) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true) do
|
||||
with %User{} <- Repo.get_by(User, nickname: username, local: true, deactivated: false) do
|
||||
conn
|
||||
|> json(true)
|
||||
else
|
||||
|
@ -26,7 +26,7 @@ def user_exists(conn, %{"user" => username}) do
|
|||
end
|
||||
|
||||
def check_password(conn, %{"user" => username, "pass" => password}) do
|
||||
with %User{password_hash: password_hash} <-
|
||||
with %User{password_hash: password_hash, deactivated: false} <-
|
||||
Repo.get_by(User, nickname: username, local: true),
|
||||
true <- Pbkdf2.checkpw(password, password_hash) do
|
||||
conn
|
||||
|
|
|
@ -25,9 +25,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
|
||||
plug(:fetch_session)
|
||||
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)
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
alias Ecto.Changeset
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
|
@ -17,6 +18,11 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
|||
|
||||
require Pleroma.Constants
|
||||
|
||||
plug(
|
||||
:skip_plug,
|
||||
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirmation_resend
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{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(
|
||||
Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
when action != :confirmation_resend
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||
)
|
||||
|
||||
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
||||
|
||||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
||||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Plugs.ExpectPublicOrAuthenticatedCheckPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
|
||||
require Logger
|
||||
|
@ -11,17 +12,20 @@ defmodule Pleroma.Web.PleromaAPI.EmojiAPIController do
|
|||
when action in [
|
||||
:create,
|
||||
:delete,
|
||||
:download_from,
|
||||
:list_from,
|
||||
:save_from,
|
||||
:import_from_fs,
|
||||
:update_file,
|
||||
: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(
|
||||
Pleroma.Config.get!([:instance, :static_dir]),
|
||||
"emoji"
|
||||
|
@ -212,13 +216,13 @@ defp shareable_packs_available(address) do
|
|||
end
|
||||
|
||||
@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`.
|
||||
|
||||
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.
|
||||
"""
|
||||
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)
|
||||
|
||||
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: ["write:accounts"]} when action != :show)
|
||||
|
||||
plug(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
@doc "GET /api/v1/pleroma/mascot"
|
||||
def show(%{assigns: %{user: user}} = conn, _params) do
|
||||
json(conn, User.get_mascot(user))
|
||||
|
|
|
@ -26,6 +26,12 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
when action in [:conversation, :conversation_statuses]
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
|
||||
when action == :emoji_reactions_by
|
||||
)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:statuses"]}
|
||||
|
@ -34,12 +40,14 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
|
|||
|
||||
plug(
|
||||
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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["write:notifications"]} when action == :mark_notifications_as_read
|
||||
)
|
||||
|
||||
def emoji_reactions_by(%{assigns: %{user: user}} = conn, %{"id" => activity_id} = params) do
|
||||
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),
|
||||
|
@ -167,7 +175,7 @@ def update_conversation(
|
|||
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
|
||||
conn
|
||||
|> add_link_headers(participations)
|
||||
|
@ -176,7 +184,7 @@ def read_conversations(%{assigns: %{user: user}} = conn, _params) do
|
|||
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
|
||||
conn
|
||||
|> put_view(NotificationView)
|
||||
|
@ -189,7 +197,7 @@ def read_notification(%{assigns: %{user: user}} = conn, %{"id" => notification_i
|
|||
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
|
||||
notifications = Enum.take(notifications, 80)
|
||||
|
||||
|
|
|
@ -13,10 +13,12 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleController do
|
|||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read"]} when action == :user_scrobbles)
|
||||
plug(OAuthScopesPlug, %{scopes: ["write"]} when action != :user_scrobbles)
|
||||
plug(
|
||||
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
|
||||
params =
|
||||
|
|
|
@ -16,6 +16,14 @@ defmodule Pleroma.Web.Router do
|
|||
plug(Pleroma.Plugs.UserEnabledPlug)
|
||||
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
|
||||
plug(Pleroma.Plugs.OAuthPlug)
|
||||
plug(Pleroma.Plugs.BasicAuthDecoderPlug)
|
||||
|
@ -39,20 +47,22 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
pipeline :api do
|
||||
plug(:expect_public_instance_or_authentication)
|
||||
plug(:base_api)
|
||||
plug(:after_auth)
|
||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||
end
|
||||
|
||||
pipeline :authenticated_api do
|
||||
plug(:expect_authentication)
|
||||
plug(:base_api)
|
||||
plug(Pleroma.Plugs.AuthExpectedPlug)
|
||||
plug(:after_auth)
|
||||
plug(Pleroma.Plugs.EnsureAuthenticatedPlug)
|
||||
plug(Pleroma.Plugs.IdempotencyPlug)
|
||||
end
|
||||
|
||||
pipeline :admin_api do
|
||||
plug(:expect_authentication)
|
||||
plug(:base_api)
|
||||
plug(Pleroma.Plugs.AdminSecretAuthenticationPlug)
|
||||
plug(:after_auth)
|
||||
|
@ -200,24 +210,28 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
|
||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||
scope "/packs" do
|
||||
# Modifying packs
|
||||
scope "/packs" do
|
||||
pipe_through(:admin_api)
|
||||
|
||||
post("/import_from_fs", EmojiAPIController, :import_from_fs)
|
||||
|
||||
post("/:pack_name/update_file", EmojiAPIController, :update_file)
|
||||
post("/:pack_name/update_metadata", EmojiAPIController, :update_metadata)
|
||||
put("/:name", EmojiAPIController, :create)
|
||||
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
|
||||
|
||||
scope "/packs" do
|
||||
# Pack info / downloading
|
||||
scope "/packs" do
|
||||
get("/", EmojiAPIController, :list_packs)
|
||||
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
|
||||
|
||||
|
@ -277,7 +291,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
|
||||
get("/conversations/:id", PleromaAPIController, :conversation)
|
||||
post("/conversations/read", PleromaAPIController, :read_conversations)
|
||||
post("/conversations/read", PleromaAPIController, :mark_conversations_as_read)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
@ -286,7 +300,7 @@ defmodule Pleroma.Web.Router do
|
|||
patch("/conversations/:id", PleromaAPIController, :update_conversation)
|
||||
put("/statuses/:id/reactions/:emoji", PleromaAPIController, :react_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_banner", AccountController, :update_banner)
|
||||
|
@ -322,53 +336,84 @@ defmodule Pleroma.Web.Router do
|
|||
pipe_through(:authenticated_api)
|
||||
|
||||
get("/accounts/verify_credentials", AccountController, :verify_credentials)
|
||||
patch("/accounts/update_credentials", AccountController, :update_credentials)
|
||||
|
||||
get("/accounts/relationships", AccountController, :relationships)
|
||||
|
||||
get("/accounts/:id/lists", AccountController, :lists)
|
||||
get("/accounts/:id/identity_proofs", AccountController, :identity_proofs)
|
||||
|
||||
get("/follow_requests", FollowRequestController, :index)
|
||||
get("/endorsements", AccountController, :endorsements)
|
||||
get("/blocks", AccountController, :blocks)
|
||||
get("/mutes", AccountController, :mutes)
|
||||
|
||||
get("/timelines/home", TimelineController, :home)
|
||||
get("/timelines/direct", TimelineController, :direct)
|
||||
post("/follows", AccountController, :follow_by_uri)
|
||||
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("/bookmarks", StatusController, :bookmarks)
|
||||
get("/apps/verify_credentials", AppController, :verify_credentials)
|
||||
|
||||
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/:id", NotificationController, :show)
|
||||
|
||||
post("/notifications/:id/dismiss", NotificationController, :dismiss)
|
||||
post("/notifications/clear", NotificationController, :clear)
|
||||
delete("/notifications/destroy_multiple", NotificationController, :destroy_multiple)
|
||||
# Deprecated: was removed in Mastodon v3, use `/notifications/:id/dismiss` instead
|
||||
post("/notifications/dismiss", NotificationController, :dismiss)
|
||||
|
||||
post("/polls/:id/votes", PollController, :vote)
|
||||
|
||||
post("/reports", ReportController, :create)
|
||||
|
||||
get("/scheduled_statuses", ScheduledActivityController, :index)
|
||||
get("/scheduled_statuses/:id", ScheduledActivityController, :show)
|
||||
|
||||
get("/lists", ListController, :index)
|
||||
get("/lists/:id", ListController, :show)
|
||||
get("/lists/:id/accounts", ListController, :list_accounts)
|
||||
put("/scheduled_statuses/:id", ScheduledActivityController, :update)
|
||||
delete("/scheduled_statuses/:id", ScheduledActivityController, :delete)
|
||||
|
||||
get("/domain_blocks", DomainBlockController, :index)
|
||||
|
||||
get("/filters", FilterController, :index)
|
||||
|
||||
get("/suggestions", SuggestionController, :index)
|
||||
|
||||
get("/conversations", ConversationController, :index)
|
||||
post("/conversations/:id/read", ConversationController, :read)
|
||||
|
||||
get("/endorsements", AccountController, :endorsements)
|
||||
|
||||
patch("/accounts/update_credentials", AccountController, :update_credentials)
|
||||
# Unlike `GET /api/v1/accounts/:id/favourites`, demands authentication
|
||||
get("/favourites", StatusController, :favourites)
|
||||
get("/bookmarks", StatusController, :bookmarks)
|
||||
|
||||
post("/statuses", StatusController, :create)
|
||||
delete("/statuses/:id", StatusController, :delete)
|
||||
|
||||
post("/statuses/:id/reblog", StatusController, :reblog)
|
||||
post("/statuses/:id/unreblog", StatusController, :unreblog)
|
||||
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/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)
|
||||
get("/push/subscription", SubscriptionController, :get)
|
||||
put("/push/subscription", SubscriptionController, :update)
|
||||
delete("/push/subscription", SubscriptionController, :delete)
|
||||
|
||||
get("/markers", MarkerController, :index)
|
||||
post("/markers", MarkerController, :upsert)
|
||||
get("/suggestions", SuggestionController, :index)
|
||||
|
||||
get("/timelines/home", TimelineController, :home)
|
||||
get("/timelines/direct", TimelineController, :direct)
|
||||
get("/timelines/list/:list_id", TimelineController, :list)
|
||||
end
|
||||
|
||||
scope "/api/web", Pleroma.Web do
|
||||
|
@ -434,15 +446,24 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:api)
|
||||
|
||||
post("/accounts", AccountController, :create)
|
||||
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/peers", InstanceController, :peers)
|
||||
|
||||
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/favourited_by", StatusController, :favourited_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/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("/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
|
||||
|
||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||
|
@ -507,7 +516,11 @@ defmodule Pleroma.Web.Router do
|
|||
get("/oauth_tokens", TwitterAPI.Controller, :oauth_tokens)
|
||||
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
|
||||
|
||||
pipeline :ostatus do
|
||||
|
@ -647,11 +660,28 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
# Test-only routes needed to test action dispatching and plug chain execution
|
||||
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
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
for action <- [:skipped_oauth, :performed_oauth, :missed_oauth] do
|
||||
get("/#{action}", OAuthTestController, action)
|
||||
for action <- @test_actions do
|
||||
get("/#{action}", AuthTestController, action)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,13 +25,6 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
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(
|
||||
|
|
|
@ -12,73 +12,57 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||
require Pleroma.Constants
|
||||
|
||||
def register_user(params, opts \\ []) do
|
||||
token = params["token"]
|
||||
trusted_app? = params["trusted_app"]
|
||||
params =
|
||||
params
|
||||
|> Map.take([
|
||||
:nickname,
|
||||
:password,
|
||||
:captcha_solution,
|
||||
:captcha_token,
|
||||
:captcha_answer_data,
|
||||
:token,
|
||||
:email,
|
||||
:trusted_app
|
||||
])
|
||||
|> Map.put(:bio, User.parse_bio(params[:bio] || ""))
|
||||
|> Map.put(:name, params.fullname)
|
||||
|> Map.put(:password_confirmation, params[:confirm])
|
||||
|
||||
params = %{
|
||||
nickname: params["nickname"],
|
||||
name: params["fullname"],
|
||||
bio: User.parse_bio(params["bio"]),
|
||||
email: params["email"],
|
||||
password: params["password"],
|
||||
password_confirmation: params["confirm"],
|
||||
captcha_solution: params["captcha_solution"],
|
||||
captcha_token: params["captcha_token"],
|
||||
captcha_answer_data: params["captcha_answer_data"]
|
||||
}
|
||||
case validate_captcha(params) do
|
||||
:ok ->
|
||||
if Pleroma.Config.get([:instance, :registrations_open]) do
|
||||
create_user(params, opts)
|
||||
else
|
||||
create_user_with_invite(params, opts)
|
||||
end
|
||||
|
||||
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
|
||||
# true if captcha is disabled or enabled and valid, false otherwise
|
||||
captcha_ok =
|
||||
if trusted_app? || not captcha_enabled do
|
||||
{:error, error} ->
|
||||
# I have no idea how this error handling works
|
||||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
end
|
||||
end
|
||||
|
||||
defp validate_captcha(params) do
|
||||
if params[:trusted_app] || not Pleroma.Config.get([Pleroma.Captcha, :enabled]) do
|
||||
:ok
|
||||
else
|
||||
Pleroma.Captcha.validate(
|
||||
params[:captcha_token],
|
||||
params[:captcha_solution],
|
||||
params[:captcha_answer_data]
|
||||
)
|
||||
end
|
||||
|
||||
# Captcha invalid
|
||||
if captcha_ok != :ok do
|
||||
{:error, error} = captcha_ok
|
||||
# I have no idea how this error handling works
|
||||
{:error, %{error: Jason.encode!(%{captcha: [error]})}}
|
||||
else
|
||||
registration_process(
|
||||
params,
|
||||
%{
|
||||
registrations_open: Pleroma.Config.get([:instance, :registrations_open]),
|
||||
token: token
|
||||
},
|
||||
opts
|
||||
params.captcha_token,
|
||||
params.captcha_solution,
|
||||
params.captcha_answer_data
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
defp registration_process(params, %{registrations_open: true}, opts) do
|
||||
create_user(params, opts)
|
||||
end
|
||||
|
||||
defp registration_process(params, %{token: token}, opts) do
|
||||
invite =
|
||||
unless is_nil(token) do
|
||||
Repo.get_by(UserInviteToken, %{token: token})
|
||||
end
|
||||
|
||||
valid_invite? = invite && UserInviteToken.valid_invite?(invite)
|
||||
|
||||
case invite do
|
||||
nil ->
|
||||
{:error, "Invalid token"}
|
||||
|
||||
invite when valid_invite? ->
|
||||
defp create_user_with_invite(params, opts) do
|
||||
with %{token: token} when is_binary(token) <- params,
|
||||
%UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}),
|
||||
true <- UserInviteToken.valid_invite?(invite) do
|
||||
UserInviteToken.update_usage!(invite)
|
||||
create_user(params, opts)
|
||||
|
||||
_ ->
|
||||
{:error, "Expired token"}
|
||||
else
|
||||
nil -> {:error, "Invalid token"}
|
||||
_ -> {:error, "Expired token"}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
@ -13,12 +14,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
|
||||
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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
|
||||
|
||||
action_fallback(:errors)
|
||||
|
||||
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, "")
|
||||
end
|
||||
|
||||
def errors(conn, {:param_cast, _}) do
|
||||
defp errors(conn, {:param_cast, _}) do
|
||||
conn
|
||||
|> put_status(400)
|
||||
|> json("Invalid parameters")
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
defp errors(conn, _) do
|
||||
conn
|
||||
|> put_status(500)
|
||||
|> json("Something went wrong")
|
||||
|
@ -64,7 +71,10 @@ defp json_reply(conn, status, json) do
|
|||
|> send_resp(status, json)
|
||||
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)
|
||||
|
||||
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})
|
||||
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")
|
||||
end
|
||||
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# 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
|
||||
@moduledoc """
|
||||
A module that keeps using definitions for controllers,
|
||||
|
@ -20,44 +25,91 @@ defmodule Pleroma.Web do
|
|||
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
|
||||
quote do
|
||||
use Phoenix.Controller, namespace: Pleroma.Web
|
||||
|
||||
import Plug.Conn
|
||||
|
||||
import Pleroma.Web.Gettext
|
||||
import Pleroma.Web.Router.Helpers
|
||||
import Pleroma.Web.TranslationHelpers
|
||||
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
|
||||
plug(:set_put_layout)
|
||||
|
||||
defp set_put_layout(conn, _) do
|
||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
||||
end
|
||||
|
||||
# Marks a plug intentionally skipped and blocks its execution if it's present in plugs chain
|
||||
defp skip_plug(conn, plug_module) do
|
||||
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
||||
defp skip_plug(conn, plug_modules) do
|
||||
plug_modules
|
||||
|> List.wrap()
|
||||
|> Enum.reduce(
|
||||
conn,
|
||||
fn plug_module, conn ->
|
||||
try do
|
||||
plug_module.skip_plug(conn)
|
||||
rescue
|
||||
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
|
||||
|
||||
# Executed just before actual controller action, invokes before-action hooks (callbacks)
|
||||
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)
|
||||
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
|
||||
defp maybe_halt_on_missing_oauth_scopes_check(conn) do
|
||||
if Pleroma.Plugs.AuthExpectedPlug.auth_expected?(conn) &&
|
||||
not PlugHelper.plug_called_or_skipped?(conn, Pleroma.Plugs.OAuthScopesPlug) do
|
||||
if PlugHelper.plug_called?(conn, ExpectAuthenticatedCheckPlug) and
|
||||
not PlugHelper.plug_called_or_skipped?(conn, OAuthScopesPlug) do
|
||||
conn
|
||||
|> render_error(
|
||||
:forbidden,
|
||||
|
@ -132,7 +184,8 @@ def channel do
|
|||
|
||||
def plug do
|
||||
quote do
|
||||
alias Pleroma.Plugs.PlugHelper
|
||||
@behaviour Pleroma.Web.Plug
|
||||
@behaviour Plug
|
||||
|
||||
@doc """
|
||||
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
|
||||
|
||||
@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
|
||||
if PlugHelper.plug_skipped?(conn, __MODULE__) do
|
||||
conn
|
||||
else
|
||||
conn
|
||||
|> PlugHelper.append_to_private_list(PlugHelper.called_plugs_list_id(), __MODULE__)
|
||||
|> perform(options)
|
||||
conn =
|
||||
PlugHelper.append_to_private_list(
|
||||
conn,
|
||||
PlugHelper.called_plugs_list_id(),
|
||||
__MODULE__
|
||||
)
|
||||
|
||||
apply(__MODULE__, :perform, [conn, options])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
4
mix.exs
4
mix.exs
|
@ -189,7 +189,9 @@ defp deps do
|
|||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||
{:mox, "~> 0.5", only: :test},
|
||||
{:restarter, path: "./restarter"},
|
||||
{:open_api_spex, "~> 3.6"}
|
||||
{:open_api_spex,
|
||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||
ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"}
|
||||
] ++ oauth_deps()
|
||||
end
|
||||
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -74,7 +74,7 @@
|
|||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.3", "def21c10a9ed70ce22754fdeea0810dafd53c2db3219a0cd54cf5526377af1c6", [:mix], [], "hexpm", "589b5af56f4afca65217a1f3eb3fee7e79b09c40c742fddc1c312b3ac0b3399f"},
|
||||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oban": {:hex, :oban, "1.2.0", "7cca94d341be43d220571e28f69131c4afc21095b25257397f50973d3fc59b07", [:mix], [{:ecto_sql, "~> 3.1", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.14", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ba5f8b3f7d76967b3e23cf8014f6a13e4ccb33431e4808f036709a7f822362ee"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.6.0", "64205aba9f2607f71b08fd43e3351b9c5e9898ec5ef49fc0ae35890da502ade9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "126ba3473966277132079cb1d5bf1e3df9e36fe2acd00166e75fd125cecb59c5"},
|
||||
"open_api_spex": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git", "b862ebd78de0df95875cf46feb6e9607130dc2a8", [ref: "b862ebd78de0df95875cf46feb6e9607130dc2a8"]},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm", "17ef63abde837ad30680ea7f857dd9e7ced9476cdd7b0394432af4bfc241b960"},
|
||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.4", "8dd29ed783f2e12195d7e0a4640effc0a7c37e6537da491f1db01839eee6d053", [:mix], [], "hexpm", "595d09db74cb093b1903381c9de423276a931a2480a46a1a5dc7f932a2a6375b"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.13", "67271ad69b51f3719354604f4a3f968f83aa61c19199343656c9caee057ff3b8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ab765a0feddb81fc62e2116c827b5f068df85159c162bee760745276ad7ddc1b"},
|
||||
|
|
|
@ -20,7 +20,7 @@ test "it continues if a user is assigned", %{conn: conn} do
|
|||
conn = assign(conn, :user, %User{})
|
||||
ret_conn = EnsureAuthenticatedPlug.call(conn, %{})
|
||||
|
||||
assert ret_conn == conn
|
||||
refute ret_conn.halted
|
||||
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
|
||||
conn = assign(conn, :user, %User{})
|
||||
assert EnsureAuthenticatedPlug.call(conn, if_func: true_fn) == conn
|
||||
assert EnsureAuthenticatedPlug.call(conn, if_func: false_fn) == conn
|
||||
assert EnsureAuthenticatedPlug.call(conn, unless_func: true_fn) == conn
|
||||
assert EnsureAuthenticatedPlug.call(conn, unless_func: false_fn) == conn
|
||||
refute EnsureAuthenticatedPlug.call(conn, if_func: true_fn).halted
|
||||
refute EnsureAuthenticatedPlug.call(conn, if_func: false_fn).halted
|
||||
refute EnsureAuthenticatedPlug.call(conn, unless_func: true_fn).halted
|
||||
refute EnsureAuthenticatedPlug.call(conn, unless_func: false_fn).halted
|
||||
end
|
||||
|
||||
test "it continues if a user is NOT assigned but :if_func evaluates to `false`",
|
||||
%{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
|
||||
|
||||
test "it continues if a user is NOT assigned but :unless_func evaluates to `true`",
|
||||
%{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
|
||||
|
||||
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
|
||||
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
||||
|
||||
assert ret_conn == conn
|
||||
refute ret_conn.halted
|
||||
end
|
||||
|
||||
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
|
||||
|> EnsurePublicOrAuthenticatedPlug.call(%{})
|
||||
|
||||
assert ret_conn == conn
|
||||
refute ret_conn.halted
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,17 +5,12 @@
|
|||
defmodule Pleroma.Plugs.OAuthScopesPlugTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Repo
|
||||
|
||||
import Mock
|
||||
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
|
||||
with_mock OAuthScopesPlug, [:passthrough], perform: &passthrough([&1, &2]) do
|
||||
conn =
|
||||
|
@ -60,7 +55,7 @@ test "if `token.scopes` fulfills specified 'all of' conditions, " <>
|
|||
|
||||
describe "with `fallback: :proceed_unauthenticated` option, " do
|
||||
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
|
||||
user = insert(: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.assigns[:user]
|
||||
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
|
||||
|
|
|
@ -51,7 +51,19 @@ defp oauth_access(scopes, opts \\ []) do
|
|||
%{user: user, token: token, conn: conn}
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(conn, status \\ nil) do
|
||||
defp request_content_type(%{conn: conn}) do
|
||||
conn = put_req_header(conn, "content-type", "multipart/form-data")
|
||||
[conn: conn]
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(
|
||||
%{
|
||||
private: %{
|
||||
open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec}
|
||||
}
|
||||
} = conn,
|
||||
status
|
||||
) do
|
||||
content_type =
|
||||
conn
|
||||
|> Plug.Conn.get_resp_header("content-type")
|
||||
|
@ -59,10 +71,12 @@ defp json_response_and_validate_schema(conn, status \\ nil) do
|
|||
|> String.split(";")
|
||||
|> List.first()
|
||||
|
||||
status = status || conn.status
|
||||
status = Plug.Conn.Status.code(status)
|
||||
|
||||
%{private: %{open_api_spex: %{operation_id: op_id, operation_lookup: lookup, spec: spec}}} =
|
||||
conn
|
||||
unless lookup[op_id].responses[status] do
|
||||
err = "Response schema not found for #{conn.status} #{conn.method} #{conn.request_path}"
|
||||
flunk(err)
|
||||
end
|
||||
|
||||
schema = lookup[op_id].responses[status].content[content_type].schema
|
||||
json = json_response(conn, status)
|
||||
|
@ -87,6 +101,10 @@ defp json_response_and_validate_schema(conn, status \\ nil) do
|
|||
end
|
||||
end
|
||||
|
||||
defp json_response_and_validate_schema(conn, _status) do
|
||||
flunk("Response schema not found for #{conn.method} #{conn.request_path} #{conn.status}")
|
||||
end
|
||||
|
||||
defp ensure_federating_or_authenticated(conn, url, user) do
|
||||
initial_setting = Config.get([:instance, :federating])
|
||||
on_exit(fn -> Config.put([:instance, :federating], initial_setting) end)
|
||||
|
|
|
@ -766,7 +766,7 @@ test "it requires authentication if instance is NOT federating", %{
|
|||
end
|
||||
|
||||
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!()
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
|
242
test/web/auth/auth_test_controller_test.exs
Normal file
242
test/web/auth/auth_test_controller_test.exs
Normal file
|
@ -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
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
|||
|
||||
describe "updating credentials" do
|
||||
setup do: oauth_access(["write:accounts"])
|
||||
setup :request_content_type
|
||||
|
||||
test "sets user settings in a generic way", %{conn: conn} do
|
||||
res_conn =
|
||||
|
@ -25,7 +26,7 @@ test "sets user settings in a generic way", %{conn: conn} do
|
|||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
assert user_data["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
|
||||
|
||||
user = Repo.get(User, user_data["id"])
|
||||
|
@ -41,7 +42,7 @@ test "sets user settings in a generic way", %{conn: conn} do
|
|||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
|
||||
assert user_data["pleroma"]["settings_store"] ==
|
||||
%{
|
||||
|
@ -62,7 +63,7 @@ test "sets user settings in a generic way", %{conn: conn} do
|
|||
}
|
||||
})
|
||||
|
||||
assert user_data = json_response(res_conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(res_conn, 200)
|
||||
|
||||
assert user_data["pleroma"]["settings_store"] ==
|
||||
%{
|
||||
|
@ -79,7 +80,7 @@ test "updates the user's bio", %{conn: conn} do
|
|||
"note" => "I drink #cofe with @#{user2.nickname}\n\nsuya.."
|
||||
})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert user_data["note"] ==
|
||||
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe">#cofe</a> with <span class="h-card"><a class="u-url mention" data-user="#{
|
||||
|
@ -90,7 +91,7 @@ test "updates the user's bio", %{conn: conn} do
|
|||
test "updates the user's locking status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{locked: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["locked"] == true
|
||||
end
|
||||
|
||||
|
@ -100,21 +101,21 @@ test "updates the user's allow_following_move", %{user: user, conn: conn} do
|
|||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{allow_following_move: "false"})
|
||||
|
||||
assert refresh_record(user).allow_following_move == false
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["allow_following_move"] == false
|
||||
end
|
||||
|
||||
test "updates the user's default scope", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{default_scope: "unlisted"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data["source"]["privacy"] == "cofe"
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["privacy"] == "unlisted"
|
||||
end
|
||||
|
||||
test "updates the user's hide_followers status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_followers: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_followers"] == true
|
||||
end
|
||||
|
||||
|
@ -122,12 +123,12 @@ test "updates the user's discoverable status", %{conn: conn} do
|
|||
assert %{"source" => %{"pleroma" => %{"discoverable" => true}}} =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "true"})
|
||||
|> json_response(:ok)
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
|
||||
assert %{"source" => %{"pleroma" => %{"discoverable" => false}}} =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{discoverable: "false"})
|
||||
|> json_response(:ok)
|
||||
|> json_response_and_validate_schema(:ok)
|
||||
end
|
||||
|
||||
test "updates the user's hide_followers_count and hide_follows_count", %{conn: conn} do
|
||||
|
@ -137,7 +138,7 @@ test "updates the user's hide_followers_count and hide_follows_count", %{conn: c
|
|||
hide_follows_count: "true"
|
||||
})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_followers_count"] == true
|
||||
assert user_data["pleroma"]["hide_follows_count"] == true
|
||||
end
|
||||
|
@ -146,7 +147,7 @@ test "updates the user's skip_thread_containment option", %{user: user, conn: co
|
|||
response =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert response["pleroma"]["skip_thread_containment"] == true
|
||||
assert refresh_record(user).skip_thread_containment
|
||||
|
@ -155,28 +156,28 @@ test "updates the user's skip_thread_containment option", %{user: user, conn: co
|
|||
test "updates the user's hide_follows status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_follows: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_follows"] == true
|
||||
end
|
||||
|
||||
test "updates the user's hide_favorites status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["pleroma"]["hide_favorites"] == true
|
||||
end
|
||||
|
||||
test "updates the user's show_role status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{show_role: "false"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["pleroma"]["show_role"] == false
|
||||
end
|
||||
|
||||
test "updates the user's no_rich_text status", %{conn: conn} do
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["source"]["pleroma"]["no_rich_text"] == true
|
||||
end
|
||||
|
||||
|
@ -184,7 +185,7 @@ test "updates the user's name", %{conn: conn} do
|
|||
conn =
|
||||
patch(conn, "/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
assert user_data["display_name"] == "markorepairs"
|
||||
end
|
||||
|
||||
|
@ -197,7 +198,7 @@ test "updates the user's avatar", %{user: user, conn: conn} do
|
|||
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["avatar"] != User.avatar_url(user)
|
||||
end
|
||||
|
||||
|
@ -210,7 +211,7 @@ test "updates the user's banner", %{user: user, conn: conn} do
|
|||
|
||||
conn = patch(conn, "/api/v1/accounts/update_credentials", %{"header" => new_header})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["header"] != User.banner_url(user)
|
||||
end
|
||||
|
||||
|
@ -226,7 +227,7 @@ test "updates the user's background", %{conn: conn} do
|
|||
"pleroma_background_image" => new_header
|
||||
})
|
||||
|
||||
assert user_response = json_response(conn, 200)
|
||||
assert user_response = json_response_and_validate_schema(conn, 200)
|
||||
assert user_response["pleroma"]["background_image"]
|
||||
end
|
||||
|
||||
|
@ -237,14 +238,15 @@ test "requires 'write:accounts' permission" do
|
|||
for token <- [token1, token2] do
|
||||
conn =
|
||||
build_conn()
|
||||
|> put_req_header("content-type", "multipart/form-data")
|
||||
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||
|> patch("/api/v1/accounts/update_credentials", %{})
|
||||
|
||||
if token == token1 do
|
||||
assert %{"error" => "Insufficient permissions: write:accounts."} ==
|
||||
json_response(conn, 403)
|
||||
json_response_and_validate_schema(conn, 403)
|
||||
else
|
||||
assert json_response(conn, 200)
|
||||
assert json_response_and_validate_schema(conn, 200)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -259,11 +261,11 @@ test "updates profile emojos", %{user: user, conn: conn} do
|
|||
"display_name" => name
|
||||
})
|
||||
|
||||
assert json_response(ret_conn, 200)
|
||||
assert json_response_and_validate_schema(ret_conn, 200)
|
||||
|
||||
conn = get(conn, "/api/v1/accounts/#{user.id}")
|
||||
|
||||
assert user_data = json_response(conn, 200)
|
||||
assert user_data = json_response_and_validate_schema(conn, 200)
|
||||
|
||||
assert user_data["note"] == note
|
||||
assert user_data["display_name"] == name
|
||||
|
@ -279,7 +281,7 @@ test "update fields", %{conn: conn} do
|
|||
account_data =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account_data["fields"] == [
|
||||
%{"name" => "<a href=\"http://google.com\">foo</a>", "value" => "bar"},
|
||||
|
@ -312,7 +314,7 @@ test "update fields via x-www-form-urlencoded", %{conn: conn} do
|
|||
conn
|
||||
|> put_req_header("content-type", "application/x-www-form-urlencoded")
|
||||
|> patch("/api/v1/accounts/update_credentials", fields)
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account["fields"] == [
|
||||
%{"name" => "foo", "value" => "bar"},
|
||||
|
@ -337,7 +339,7 @@ test "update fields with empty name", %{conn: conn} do
|
|||
account =
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(200)
|
||||
|> json_response_and_validate_schema(200)
|
||||
|
||||
assert account["fields"] == [
|
||||
%{"name" => "foo", "value" => ""}
|
||||
|
@ -356,14 +358,14 @@ test "update fields when invalid request", %{conn: conn} do
|
|||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
|
||||
fields = [%{"name" => long_name, "value" => "bar"}]
|
||||
|
||||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
|
||||
Pleroma.Config.put([:instance, :max_account_fields], 1)
|
||||
|
||||
|
@ -375,7 +377,7 @@ test "update fields when invalid request", %{conn: conn} do
|
|||
assert %{"error" => "Invalid request"} ==
|
||||
conn
|
||||
|> patch("/api/v1/accounts/update_credentials", %{"fields_attributes" => fields})
|
||||
|> json_response(403)
|
||||
|> json_response_and_validate_schema(403)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,8 +4,6 @@
|
|||
|
||||
defmodule Pleroma.Web.MastodonAPI.CustomEmojiControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
alias Pleroma.Web.ApiSpec
|
||||
import OpenApiSpex.TestAssertions
|
||||
|
||||
test "with tags", %{conn: conn} do
|
||||
assert resp =
|
||||
|
@ -21,6 +19,5 @@ test "with tags", %{conn: conn} do
|
|||
assert Map.has_key?(emoji, "category")
|
||||
assert Map.has_key?(emoji, "url")
|
||||
assert Map.has_key?(emoji, "visible_in_picker")
|
||||
assert_schema(emoji, "CustomEmoji", ApiSpec.spec())
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,35 +7,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
|
||||
describe "empty_array/2 (stubs)" 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
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/accounts/#{user.id}/identity_proofs")
|
||||
|> json_response(200)
|
||||
|
||||
assert res == []
|
||||
end
|
||||
|
||||
test "GET /api/v1/endorsements" do
|
||||
%{conn: conn} = oauth_access(["read:accounts"])
|
||||
|
||||
res =
|
||||
assert [] ==
|
||||
conn
|
||||
|> get("/api/v1/endorsements")
|
||||
|> json_response(200)
|
||||
|
||||
assert res == []
|
||||
end
|
||||
|
||||
test "GET /api/v1/trends", %{conn: conn} do
|
||||
res =
|
||||
assert [] ==
|
||||
conn
|
||||
|> get("/api/v1/trends")
|
||||
|> json_response(200)
|
||||
|
||||
assert res == []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.MongooseIMController do
|
|||
test "/user_exists", %{conn: conn} do
|
||||
_user = insert(:user, nickname: "lain")
|
||||
_remote_user = insert(:user, nickname: "alice", local: false)
|
||||
_deactivated_user = insert(:user, nickname: "konata", deactivated: true)
|
||||
|
||||
res =
|
||||
conn
|
||||
|
@ -30,11 +31,25 @@ test "/user_exists", %{conn: conn} do
|
|||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :user_exists), user: "konata")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
end
|
||||
|
||||
test "/check_password", %{conn: conn} do
|
||||
user = insert(:user, password_hash: Comeonin.Pbkdf2.hashpwsalt("cool"))
|
||||
|
||||
_deactivated_user =
|
||||
insert(:user,
|
||||
nickname: "konata",
|
||||
deactivated: true,
|
||||
password_hash: Comeonin.Pbkdf2.hashpwsalt("cool")
|
||||
)
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: user.nickname, pass: "cool")
|
||||
|
@ -49,6 +64,13 @@ test "/check_password", %{conn: conn} do
|
|||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: "konata", pass: "cool")
|
||||
|> json_response(404)
|
||||
|
||||
assert res == false
|
||||
|
||||
res =
|
||||
conn
|
||||
|> get(mongoose_im_path(conn, :check_password), user: "nobody", pass: "cool")
|
||||
|
|
|
@ -151,15 +151,18 @@ test "returns list of statuses favorited by specified user", %{
|
|||
assert like["id"] == activity.id
|
||||
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
|
||||
} do
|
||||
activity = insert(:note_activity)
|
||||
CommonAPI.favorite(user, activity.id)
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||
|> json_response(403)
|
||||
|> json_response(200)
|
||||
|
||||
assert length(response) == 1
|
||||
end
|
||||
|
||||
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
|
||||
end
|
||||
|
||||
response =
|
||||
build_conn()
|
||||
|> get("/api/v1/pleroma/accounts/#{user.id}/favourites")
|
||||
|> json_response(403)
|
||||
|> json_response(200)
|
||||
|
||||
assert length(response) == 0
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
test "listing remote packs" do
|
||||
admin = insert(:user, is_admin: true)
|
||||
%{conn: conn} = oauth_access(["admin:write"], user: admin)
|
||||
conn = build_conn()
|
||||
|
||||
resp =
|
||||
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)
|
||||
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 ->
|
||||
File.rm_rf!("#{@emoji_dir_path}/test_pack2")
|
||||
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(
|
||||
emoji_api_path(
|
||||
conn,
|
||||
:download_from
|
||||
:save_from
|
||||
),
|
||||
%{
|
||||
instance_address: "https://old-instance",
|
||||
|
@ -152,7 +151,7 @@ test "downloading shared & unshared packs from another instance via download_fro
|
|||
|> post(
|
||||
emoji_api_path(
|
||||
conn,
|
||||
:download_from
|
||||
:save_from
|
||||
),
|
||||
%{
|
||||
instance_address: "https://example.com",
|
||||
|
@ -179,7 +178,7 @@ test "downloading shared & unshared packs from another instance via download_fro
|
|||
|> post(
|
||||
emoji_api_path(
|
||||
conn,
|
||||
:download_from
|
||||
:save_from
|
||||
),
|
||||
%{
|
||||
instance_address: "https://example.com",
|
||||
|
|
|
@ -19,13 +19,9 @@ test "without valid credentials", %{conn: conn} do
|
|||
end
|
||||
|
||||
test "with credentials, without any params" do
|
||||
%{user: current_user, conn: conn} =
|
||||
oauth_access(["read:notifications", "write:notifications"])
|
||||
%{conn: conn} = oauth_access(["write:notifications"])
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, current_user)
|
||||
|> post("/api/qvitter/statuses/notifications/read")
|
||||
conn = post(conn, "/api/qvitter/statuses/notifications/read")
|
||||
|
||||
assert json_response(conn, 400) == %{
|
||||
"error" => "You need to specify latest_id",
|
||||
|
|
|
@ -18,11 +18,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
|
||||
test "it registers a new user and returns the user." do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -35,12 +35,12 @@ test "it registers a new user and returns the user." do
|
|||
|
||||
test "it registers a new user with empty string in bio and returns the user." do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -60,12 +60,12 @@ test "it sends confirmation email if :account_activation_required is specified i
|
|||
end
|
||||
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -87,23 +87,23 @@ test "it sends confirmation email if :account_activation_required is specified i
|
|||
|
||||
test "it registers a new user and parses mentions in the bio" do
|
||||
data1 = %{
|
||||
"nickname" => "john",
|
||||
"email" => "john@gmail.com",
|
||||
"fullname" => "John Doe",
|
||||
"bio" => "test",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "john",
|
||||
:email => "john@gmail.com",
|
||||
:fullname => "John Doe",
|
||||
:bio => "test",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user1} = TwitterAPI.register_user(data1)
|
||||
|
||||
data2 = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "@john test",
|
||||
"password" => "bear",
|
||||
"confirm" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "@john test",
|
||||
:password => "bear",
|
||||
:confirm => "bear"
|
||||
}
|
||||
|
||||
{:ok, user2} = TwitterAPI.register_user(data2)
|
||||
|
@ -123,13 +123,13 @@ test "returns user on success" do
|
|||
{:ok, invite} = UserInviteToken.create_invite()
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -145,13 +145,13 @@ test "returns user on success" do
|
|||
|
||||
test "returns error on invalid token" do
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => "DudeLetMeInImAFairy"
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => "DudeLetMeInImAFairy"
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -165,13 +165,13 @@ test "returns error on expired token" do
|
|||
UserInviteToken.update_invite!(invite, used: true)
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -186,16 +186,16 @@ test "returns error on expired token" do
|
|||
|
||||
setup do
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees"
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees"
|
||||
}
|
||||
|
||||
check_fn = fn invite ->
|
||||
data = Map.put(data, "token", invite.token)
|
||||
data = Map.put(data, :token, invite.token)
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
fetched_user = User.get_cached_by_nickname("vinny")
|
||||
|
||||
|
@ -250,13 +250,13 @@ test "returns user on success, after him registration fails" do
|
|||
UserInviteToken.update_invite!(invite, uses: 99)
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -269,13 +269,13 @@ test "returns user on success, after him registration fails" do
|
|||
AccountView.render("show.json", %{user: fetched_user})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -292,13 +292,13 @@ test "returns user on success" do
|
|||
{:ok, invite} = UserInviteToken.create_invite(%{expires_at: Date.utc_today(), max_use: 100})
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -317,13 +317,13 @@ test "error after max uses" do
|
|||
UserInviteToken.update_invite!(invite, uses: 99)
|
||||
|
||||
data = %{
|
||||
"nickname" => "vinny",
|
||||
"email" => "pasta@pizza.vs",
|
||||
"fullname" => "Vinny Vinesauce",
|
||||
"bio" => "streamer",
|
||||
"password" => "hiptofbees",
|
||||
"confirm" => "hiptofbees",
|
||||
"token" => invite.token
|
||||
:nickname => "vinny",
|
||||
:email => "pasta@pizza.vs",
|
||||
:fullname => "Vinny Vinesauce",
|
||||
:bio => "streamer",
|
||||
:password => "hiptofbees",
|
||||
:confirm => "hiptofbees",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:ok, user} = TwitterAPI.register_user(data)
|
||||
|
@ -335,13 +335,13 @@ test "error after max uses" do
|
|||
AccountView.render("show.json", %{user: fetched_user})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -355,13 +355,13 @@ test "returns error on overdue date" do
|
|||
UserInviteToken.create_invite(%{expires_at: Date.add(Date.utc_today(), -1), max_use: 100})
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -377,13 +377,13 @@ test "returns error on with overdue date and after max" do
|
|||
UserInviteToken.update_invite!(invite, uses: 100)
|
||||
|
||||
data = %{
|
||||
"nickname" => "GrimReaper",
|
||||
"email" => "death@reapers.afterlife",
|
||||
"fullname" => "Reaper Grim",
|
||||
"bio" => "Your time has come",
|
||||
"password" => "scythe",
|
||||
"confirm" => "scythe",
|
||||
"token" => invite.token
|
||||
:nickname => "GrimReaper",
|
||||
:email => "death@reapers.afterlife",
|
||||
:fullname => "Reaper Grim",
|
||||
:bio => "Your time has come",
|
||||
:password => "scythe",
|
||||
:confirm => "scythe",
|
||||
:token => invite.token
|
||||
}
|
||||
|
||||
{:error, msg} = TwitterAPI.register_user(data)
|
||||
|
@ -395,11 +395,11 @@ test "returns error on with overdue date and after max" do
|
|||
|
||||
test "it returns the error on registration problems" do
|
||||
data = %{
|
||||
"nickname" => "lain",
|
||||
"email" => "lain@wired.jp",
|
||||
"fullname" => "lain iwakura",
|
||||
"bio" => "close the world.",
|
||||
"password" => "bear"
|
||||
:nickname => "lain",
|
||||
:email => "lain@wired.jp",
|
||||
:fullname => "lain iwakura",
|
||||
:bio => "close the world.",
|
||||
:password => "bear"
|
||||
}
|
||||
|
||||
{:error, error_object} = TwitterAPI.register_user(data)
|
||||
|
|
Loading…
Reference in a new issue