updated registration API responses
This commit is contained in:
parent
481906207e
commit
3607e453e4
43
lib/pleroma/captcha/error.ex
Normal file
43
lib/pleroma/captcha/error.ex
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Captcha.Error do
|
||||||
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
|
def message(_reason, opts \\ [])
|
||||||
|
|
||||||
|
def message(:missing_field, %{name: name}) do
|
||||||
|
dgettext(
|
||||||
|
"errors",
|
||||||
|
"Invalid CAPTCHA (Missing parameter: %{name})",
|
||||||
|
name: name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:captcha_error, _) do
|
||||||
|
dgettext("errors", "CAPTCHA Error")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:invalid, _) do
|
||||||
|
dgettext("errors", "Invalid CAPTCHA")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:kocaptcha_service_unavailable, _) do
|
||||||
|
dgettext("errors", "Kocaptcha service unavailable")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:expired, _) do
|
||||||
|
dgettext("errors", "CAPTCHA expired")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:already_used, _) do
|
||||||
|
dgettext("errors", "CAPTCHA already used")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(:invalid_answer_data, _) do
|
||||||
|
dgettext("errors", "Invalid answer data")
|
||||||
|
end
|
||||||
|
|
||||||
|
def message(error, _), do: error
|
||||||
|
end
|
33
lib/pleroma/web/api_spec/errors/registration_user_error.ex
Normal file
33
lib/pleroma/web/api_spec/errors/registration_user_error.ex
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# 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.Errors.RegistrationUserError do
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
import Plug.Conn, only: [put_status: 2]
|
||||||
|
import Phoenix.Controller, only: [json: 2]
|
||||||
|
alias Pleroma.Web.ApiSpec.RenderError
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
@impl Plug
|
||||||
|
|
||||||
|
def call(conn, errors) do
|
||||||
|
field_errors =
|
||||||
|
errors
|
||||||
|
|> Enum.group_by(& &1.name)
|
||||||
|
|> Enum.into(%{}, fn {field, field_errors} ->
|
||||||
|
{field, Enum.map(field_errors, &RenderError.message/1)}
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{
|
||||||
|
error: "Please review the submission",
|
||||||
|
identifier: "review_submission",
|
||||||
|
fields: field_errors
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
@ -34,7 +34,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
requestBody: request_body("Parameters", create_request(), required: true),
|
requestBody: request_body("Parameters", create_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Account", "application/json", create_response()),
|
200 => Operation.response("Account", "application/json", create_response()),
|
||||||
400 => Operation.response("Error", "application/json", ApiError),
|
400 => Operation.response("Error", "application/json", error_response()),
|
||||||
403 => Operation.response("Error", "application/json", ApiError),
|
403 => Operation.response("Error", "application/json", ApiError),
|
||||||
429 => Operation.response("Error", "application/json", ApiError)
|
429 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
@ -453,6 +453,35 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp error_response do
|
||||||
|
%Schema{
|
||||||
|
title: "AccountCreateErrorResponse",
|
||||||
|
description: "Response schema for errors",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
identifier: %Schema{type: :string},
|
||||||
|
message: %Schema{type: :string},
|
||||||
|
fields: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
captcha: %Schema{type: :array, items: %Schema{type: :string}},
|
||||||
|
email: %Schema{type: :array, items: %Schema{type: :string}},
|
||||||
|
invite: %Schema{type: :array, items: %Schema{type: :string}},
|
||||||
|
password: %Schema{type: :array, items: %Schema{type: :string}},
|
||||||
|
username: %Schema{type: :array, items: %Schema{type: :string}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"identifier" => "review_submission",
|
||||||
|
"fields" => %{
|
||||||
|
"captcha" => ["Invalid CAPTCHA"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
# Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
|
# Note: this is a token response (if login succeeds!), but there's no oauth operation file yet.
|
||||||
defp create_response do
|
defp create_response do
|
||||||
%Schema{
|
%Schema{
|
||||||
|
@ -47,14 +47,14 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_schema_type, type: type, name: name}) do
|
def message(%{reason: :invalid_schema_type, type: type, name: name}) do
|
||||||
gettext("%{name} - Invalid schema.type. Got: %{type}.",
|
gettext("%{name} - Invalid schema.type. Got: %{type}.",
|
||||||
name: name,
|
name: name,
|
||||||
type: inspect(type)
|
type: inspect(type)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :null_value, name: name} = error) do
|
def message(%{reason: :null_value, name: name} = error) do
|
||||||
case error.type do
|
case error.type do
|
||||||
nil ->
|
nil ->
|
||||||
gettext("%{name} - null value.", name: name)
|
gettext("%{name} - null value.", name: name)
|
||||||
@ -67,42 +67,42 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
|
def message(%{reason: :all_of, meta: %{invalid_schema: invalid_schema}}) do
|
||||||
gettext(
|
gettext(
|
||||||
"Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
|
"Failed to cast value as %{invalid_schema}. Value must be castable using `allOf` schemas listed.",
|
||||||
invalid_schema: invalid_schema
|
invalid_schema: invalid_schema
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
|
def message(%{reason: :any_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||||
gettext("Failed to cast value using any of: %{failed_schemas}.",
|
gettext("Failed to cast value using any of: %{failed_schemas}.",
|
||||||
failed_schemas: failed_schemas
|
failed_schemas: failed_schemas
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
|
def message(%{reason: :one_of, meta: %{failed_schemas: failed_schemas}}) do
|
||||||
gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
|
gettext("Failed to cast value to one of: %{failed_schemas}.", failed_schemas: failed_schemas)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :min_length, length: length, name: name}) do
|
def message(%{reason: :min_length, length: length, name: name}) do
|
||||||
gettext("%{name} - String length is smaller than minLength: %{length}.",
|
gettext("%{name} - String length is smaller than minLength: %{length}.",
|
||||||
name: name,
|
name: name,
|
||||||
length: length
|
length: length
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :max_length, length: length, name: name}) do
|
def message(%{reason: :max_length, length: length, name: name}) do
|
||||||
gettext("%{name} - String length is larger than maxLength: %{length}.",
|
gettext("%{name} - String length is larger than maxLength: %{length}.",
|
||||||
name: name,
|
name: name,
|
||||||
length: length
|
length: length
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :unique_items, name: name}) do
|
def message(%{reason: :unique_items, name: name}) do
|
||||||
gettext("%{name} - Array items must be unique.", name: name)
|
gettext("%{name} - Array items must be unique.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :min_items, length: min, value: array, name: name}) do
|
def message(%{reason: :min_items, length: min, value: array, name: name}) do
|
||||||
gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
|
gettext("%{name} - Array length %{length} is smaller than minItems: %{min}.",
|
||||||
name: name,
|
name: name,
|
||||||
length: length(array),
|
length: length(array),
|
||||||
@ -110,7 +110,7 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :max_items, length: max, value: array, name: name}) do
|
def message(%{reason: :max_items, length: max, value: array, name: name}) do
|
||||||
gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
|
gettext("%{name} - Array length %{length} is larger than maxItems: %{}.",
|
||||||
name: name,
|
name: name,
|
||||||
length: length(array),
|
length: length(array),
|
||||||
@ -118,7 +118,7 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
|
def message(%{reason: :multiple_of, length: multiple, value: count, name: name}) do
|
||||||
gettext("%{name} - %{count} is not a multiple of %{multiple}.",
|
gettext("%{name} - %{count} is not a multiple of %{multiple}.",
|
||||||
name: name,
|
name: name,
|
||||||
count: count,
|
count: count,
|
||||||
@ -126,8 +126,8 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :exclusive_max, length: max, value: value, name: name})
|
def message(%{reason: :exclusive_max, length: max, value: value, name: name})
|
||||||
when value >= max do
|
when value >= max do
|
||||||
gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
|
gettext("%{name} - %{value} is larger than exclusive maximum %{max}.",
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
@ -135,8 +135,8 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :maximum, length: max, value: value, name: name})
|
def message(%{reason: :maximum, length: max, value: value, name: name})
|
||||||
when value > max do
|
when value > max do
|
||||||
gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
|
gettext("%{name} - %{value} is larger than inclusive maximum %{max}.",
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
@ -144,8 +144,8 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
|
def message(%{reason: :exclusive_multiple, length: min, value: value, name: name})
|
||||||
when value <= min do
|
when value <= min do
|
||||||
gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
|
gettext("%{name} - %{value} is smaller than exclusive minimum %{min}.",
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
@ -153,8 +153,8 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :minimum, length: min, value: value, name: name})
|
def message(%{reason: :minimum, length: min, value: value, name: name})
|
||||||
when value < min do
|
when value < min do
|
||||||
gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
|
gettext("%{name} - %{value} is smaller than inclusive minimum %{min}.",
|
||||||
name: name,
|
name: name,
|
||||||
value: value,
|
value: value,
|
||||||
@ -162,7 +162,7 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_type, type: type, value: value, name: name}) do
|
def message(%{reason: :invalid_type, type: type, value: value, name: name}) do
|
||||||
gettext("%{name} - Invalid %{type}. Got: %{value}.",
|
gettext("%{name} - Invalid %{type}. Got: %{value}.",
|
||||||
name: name,
|
name: name,
|
||||||
value: OpenApiSpex.TermType.type(value),
|
value: OpenApiSpex.TermType.type(value),
|
||||||
@ -170,49 +170,49 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_format, format: format, name: name}) do
|
def message(%{reason: :invalid_format, format: format, name: name}) do
|
||||||
gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
|
gettext("%{name} - Invalid format. Expected %{format}.", name: name, format: inspect(format))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_enum, name: name}) do
|
def message(%{reason: :invalid_enum, name: name}) do
|
||||||
gettext("%{name} - Invalid value for enum.", name: name)
|
gettext("%{name} - Invalid value for enum.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
|
def message(%{reason: :polymorphic_failed, type: polymorphic_type}) do
|
||||||
gettext("Failed to cast to any schema in %{polymorphic_type}",
|
gettext("Failed to cast to any schema in %{polymorphic_type}",
|
||||||
polymorphic_type: polymorphic_type
|
polymorphic_type: polymorphic_type
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :unexpected_field, name: name}) do
|
def message(%{reason: :unexpected_field, name: name}) do
|
||||||
gettext("Unexpected field: %{name}.", name: safe_string(name))
|
gettext("Unexpected field: %{name}.", name: safe_string(name))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :no_value_for_discriminator, name: field}) do
|
def message(%{reason: :no_value_for_discriminator, name: field}) do
|
||||||
gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
|
gettext("Value used as discriminator for `%{field}` matches no schemas.", name: field)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_discriminator_value, name: field}) do
|
def message(%{reason: :invalid_discriminator_value, name: field}) do
|
||||||
gettext("No value provided for required discriminator `%{field}`.", name: field)
|
gettext("No value provided for required discriminator `%{field}`.", name: field)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :unknown_schema, name: name}) do
|
def message(%{reason: :unknown_schema, name: name}) do
|
||||||
gettext("Unknown schema: %{name}.", name: name)
|
gettext("Unknown schema: %{name}.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :missing_field, name: name}) do
|
def message(%{reason: :missing_field, name: name}) do
|
||||||
gettext("Missing field: %{name}.", name: name)
|
gettext("Missing field: %{name}.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :missing_header, name: name}) do
|
def message(%{reason: :missing_header, name: name}) do
|
||||||
gettext("Missing header: %{name}.", name: name)
|
gettext("Missing header: %{name}.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :invalid_header, name: name}) do
|
def message(%{reason: :invalid_header, name: name}) do
|
||||||
gettext("Invalid value for header: %{name}.", name: name)
|
gettext("Invalid value for header: %{name}.", name: name)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :max_properties, meta: meta}) do
|
def message(%{reason: :max_properties, meta: meta}) do
|
||||||
gettext(
|
gettext(
|
||||||
"Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
|
"Object property count %{property_count} is greater than maxProperties: %{max_properties}.",
|
||||||
property_count: meta.property_count,
|
property_count: meta.property_count,
|
||||||
@ -220,7 +220,7 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp message(%{reason: :min_properties, meta: meta}) do
|
def message(%{reason: :min_properties, meta: meta}) do
|
||||||
gettext(
|
gettext(
|
||||||
"Object property count %{property_count} is less than minProperties: %{min_properties}",
|
"Object property count %{property_count} is less than minProperties: %{min_properties}",
|
||||||
property_count: meta.property_count,
|
property_count: meta.property_count,
|
||||||
|
@ -19,6 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
alias Pleroma.Web.ApiSpec.Errors.RegistrationUserError
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.MastodonAPI.ListView
|
alias Pleroma.Web.MastodonAPI.ListView
|
||||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||||
@ -31,7 +32,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
alias Pleroma.Web.Plugs.RateLimiter
|
alias Pleroma.Web.Plugs.RateLimiter
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(
|
||||||
|
Pleroma.Web.ApiSpec.CastAndValidate,
|
||||||
|
[render_error: RegistrationUserError] when action in [:create]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate when action not in [:create])
|
||||||
|
|
||||||
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
plug(:skip_plug, [OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :create)
|
||||||
|
|
||||||
@ -101,8 +107,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
with :ok <- validate_email_param(params),
|
with :ok <- validate_email_param(params),
|
||||||
:ok <- TwitterAPI.validate_captcha(app, params),
|
:ok <- TwitterAPI.validate_captcha(app, params),
|
||||||
{:ok, user} <- TwitterAPI.register_user(params),
|
{:ok, user} <- TwitterAPI.register_user(params),
|
||||||
{_, {:ok, token}} <-
|
{_, {:ok, token}} <- {:login, OAuthController.login(user, app, app.scopes)} do
|
||||||
{:login, OAuthController.login(user, app, app.scopes)} do
|
|
||||||
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
json(conn, OAuthView.render("token.json", %{user: user, token: token}))
|
||||||
else
|
else
|
||||||
{:login, {:account_status, :confirmation_pending}} ->
|
{:login, {:account_status, :confirmation_pending}} ->
|
||||||
@ -126,8 +131,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
identifier: "manual_login_required"
|
identifier: "manual_login_required"
|
||||||
})
|
})
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, errors} ->
|
||||||
json_response(conn, :bad_request, %{error: error})
|
json_response(conn, :bad_request, %{
|
||||||
|
identifier: "review_submission",
|
||||||
|
error: "Please review the submission",
|
||||||
|
fields: errors
|
||||||
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -143,8 +152,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
|||||||
|
|
||||||
defp validate_email_param(_) do
|
defp validate_email_param(_) do
|
||||||
case Pleroma.Config.get([:instance, :account_activation_required]) do
|
case Pleroma.Config.get([:instance, :account_activation_required]) do
|
||||||
true -> {:error, dgettext("errors", "Missing parameter: %{name}", name: "email")}
|
true ->
|
||||||
_ -> :ok
|
{:error,
|
||||||
|
%{
|
||||||
|
email: [dgettext("errors", "Missing parameter: %{name}", name: "email")]
|
||||||
|
}}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -3,14 +3,12 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
||||||
import Pleroma.Web.Gettext
|
|
||||||
|
|
||||||
alias Pleroma.Emails.Mailer
|
alias Pleroma.Emails.Mailer
|
||||||
alias Pleroma.Emails.UserEmail
|
alias Pleroma.Emails.UserEmail
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
@spec register_user(map(), keyword()) :: {:ok, User.t()} | {:error, map()}
|
||||||
def register_user(params, opts \\ []) do
|
def register_user(params, opts \\ []) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
@ -28,18 +26,20 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec create_user_with_invite(map(), keyword()) :: {:ok, User.t()} | {:error, map()}
|
||||||
defp create_user_with_invite(params, opts) do
|
defp create_user_with_invite(params, opts) do
|
||||||
with %{token: token} when is_binary(token) <- params,
|
with %{token: token} when is_binary(token) <- params,
|
||||||
%UserInviteToken{} = invite <- Repo.get_by(UserInviteToken, %{token: token}),
|
{:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
true <- UserInviteToken.valid_invite?(invite) do
|
true <- UserInviteToken.valid_invite?(invite) do
|
||||||
UserInviteToken.update_usage!(invite)
|
UserInviteToken.update_usage!(invite)
|
||||||
create_user(params, opts)
|
create_user(params, opts)
|
||||||
else
|
else
|
||||||
nil -> {:error, "Invalid token"}
|
nil -> {:error, %{invite: ["Invalid token"]}}
|
||||||
_ -> {:error, "Expired token"}
|
_ -> {:error, %{invite: ["Expired token"]}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec create_user(map(), keyword()) :: {:ok, User.t()} | {:error, map()}
|
||||||
defp create_user(params, opts) do
|
defp create_user(params, opts) do
|
||||||
changeset = User.register_changeset(%User{}, params, opts)
|
changeset = User.register_changeset(%User{}, params, opts)
|
||||||
|
|
||||||
@ -52,7 +52,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||||||
errors =
|
errors =
|
||||||
changeset
|
changeset
|
||||||
|> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
|
|> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
|
||||||
|> Jason.encode!()
|
|
||||||
|
|
||||||
{:error, errors}
|
{:error, errors}
|
||||||
end
|
end
|
||||||
@ -104,26 +103,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||||||
) do
|
) do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
{:error, :captcha_error} ->
|
|
||||||
captcha_error(dgettext("errors", "CAPTCHA Error"))
|
|
||||||
|
|
||||||
{:error, :invalid} ->
|
|
||||||
captcha_error(dgettext("errors", "Invalid CAPTCHA"))
|
|
||||||
|
|
||||||
{:error, :kocaptcha_service_unavailable} ->
|
|
||||||
captcha_error(dgettext("errors", "Kocaptcha service unavailable"))
|
|
||||||
|
|
||||||
{:error, :expired} ->
|
|
||||||
captcha_error(dgettext("errors", "CAPTCHA expired"))
|
|
||||||
|
|
||||||
{:error, :already_used} ->
|
|
||||||
captcha_error(dgettext("errors", "CAPTCHA already used"))
|
|
||||||
|
|
||||||
{:error, :invalid_answer_data} ->
|
|
||||||
captcha_error(dgettext("errors", "Invalid answer data"))
|
|
||||||
|
|
||||||
{:error, error} ->
|
{:error, error} ->
|
||||||
captcha_error(error)
|
{:error, %{captcha: [Pleroma.Captcha.Error.message(error)]}}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -131,12 +112,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
|
|||||||
[:captcha_solution, :captcha_token, :captcha_answer_data]
|
[:captcha_solution, :captcha_token, :captcha_answer_data]
|
||||||
|> Enum.find_value(:ok, fn key ->
|
|> Enum.find_value(:ok, fn key ->
|
||||||
unless is_binary(params[key]) do
|
unless is_binary(params[key]) do
|
||||||
error = dgettext("errors", "Invalid CAPTCHA (Missing parameter: %{name})", name: key)
|
{:error, Pleroma.Captcha.Error.message(:missing_field, %{name: key})}
|
||||||
{:error, error}
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
# For some reason FE expects error message to be a serialized JSON
|
|
||||||
defp captcha_error(error), do: {:error, Jason.encode!(%{captcha: [error]})}
|
|
||||||
end
|
end
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.OAuth.Token
|
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
@ -888,543 +886,6 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
|||||||
assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
|
assert %{"id" => _id, "blocking" => false} = json_response_and_validate_schema(conn, 200)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "create account by app" do
|
|
||||||
setup do
|
|
||||||
valid_params = %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true
|
|
||||||
}
|
|
||||||
|
|
||||||
[valid_params: valid_params]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "registers and logs in without :account_activation_required / :account_approval_required",
|
|
||||||
%{conn: conn} do
|
|
||||||
clear_config([:instance, :account_activation_required], false)
|
|
||||||
clear_config([:instance, :account_approval_required], false)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> post("/api/v1/apps", %{
|
|
||||||
client_name: "client_name",
|
|
||||||
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
scopes: "read, write, follow"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => client_id,
|
|
||||||
"client_secret" => client_secret,
|
|
||||||
"id" => _,
|
|
||||||
"name" => "client_name",
|
|
||||||
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
"vapid_key" => _,
|
|
||||||
"website" => nil
|
|
||||||
} = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
post(conn, "/oauth/token", %{
|
|
||||||
grant_type: "client_credentials",
|
|
||||||
client_id: client_id,
|
|
||||||
client_secret: client_secret
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
|
||||||
json_response(conn, 200)
|
|
||||||
|
|
||||||
assert token
|
|
||||||
token_from_db = Repo.get_by(Token, token: token)
|
|
||||||
assert token_from_db
|
|
||||||
assert refresh
|
|
||||||
assert scope == "read write follow"
|
|
||||||
|
|
||||||
clear_config([User, :email_blacklist], ["example.org"])
|
|
||||||
|
|
||||||
params = %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
bio: "Test Bio",
|
|
||||||
agreement: true
|
|
||||||
}
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> put_req_header("authorization", "Bearer " <> token)
|
|
||||||
|> post("/api/v1/accounts", params)
|
|
||||||
|
|
||||||
assert %{"error" => "{\"email\":[\"Invalid email\"]}"} =
|
|
||||||
json_response_and_validate_schema(conn, 400)
|
|
||||||
|
|
||||||
Pleroma.Config.put([User, :email_blacklist], [])
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> put_req_header("authorization", "Bearer " <> token)
|
|
||||||
|> post("/api/v1/accounts", params)
|
|
||||||
|
|
||||||
%{
|
|
||||||
"access_token" => token,
|
|
||||||
"created_at" => _created_at,
|
|
||||||
"scope" => ^scope,
|
|
||||||
"token_type" => "Bearer"
|
|
||||||
} = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
|
||||||
token_from_db = Repo.get_by(Token, token: token)
|
|
||||||
assert token_from_db
|
|
||||||
user = Repo.preload(token_from_db, :user).user
|
|
||||||
|
|
||||||
assert user
|
|
||||||
refute user.confirmation_pending
|
|
||||||
refute user.approval_pending
|
|
||||||
end
|
|
||||||
|
|
||||||
test "registers but does not log in with :account_activation_required", %{conn: conn} do
|
|
||||||
clear_config([:instance, :account_activation_required], true)
|
|
||||||
clear_config([:instance, :account_approval_required], false)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> post("/api/v1/apps", %{
|
|
||||||
client_name: "client_name",
|
|
||||||
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
scopes: "read, write, follow"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => client_id,
|
|
||||||
"client_secret" => client_secret,
|
|
||||||
"id" => _,
|
|
||||||
"name" => "client_name",
|
|
||||||
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
"vapid_key" => _,
|
|
||||||
"website" => nil
|
|
||||||
} = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
post(conn, "/oauth/token", %{
|
|
||||||
grant_type: "client_credentials",
|
|
||||||
client_id: client_id,
|
|
||||||
client_secret: client_secret
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
|
||||||
json_response(conn, 200)
|
|
||||||
|
|
||||||
assert token
|
|
||||||
token_from_db = Repo.get_by(Token, token: token)
|
|
||||||
assert token_from_db
|
|
||||||
assert refresh
|
|
||||||
assert scope == "read write follow"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> put_req_header("authorization", "Bearer " <> token)
|
|
||||||
|> post("/api/v1/accounts", %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
bio: "Test Bio",
|
|
||||||
agreement: true
|
|
||||||
})
|
|
||||||
|
|
||||||
response = json_response_and_validate_schema(conn, 200)
|
|
||||||
assert %{"identifier" => "missing_confirmed_email"} = response
|
|
||||||
refute response["access_token"]
|
|
||||||
refute response["token_type"]
|
|
||||||
|
|
||||||
user = Repo.get_by(User, email: "lain@example.org")
|
|
||||||
assert user.confirmation_pending
|
|
||||||
end
|
|
||||||
|
|
||||||
test "registers but does not log in with :account_approval_required", %{conn: conn} do
|
|
||||||
clear_config([:instance, :account_approval_required], true)
|
|
||||||
clear_config([:instance, :account_activation_required], false)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> post("/api/v1/apps", %{
|
|
||||||
client_name: "client_name",
|
|
||||||
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
scopes: "read, write, follow"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"client_id" => client_id,
|
|
||||||
"client_secret" => client_secret,
|
|
||||||
"id" => _,
|
|
||||||
"name" => "client_name",
|
|
||||||
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
|
||||||
"vapid_key" => _,
|
|
||||||
"website" => nil
|
|
||||||
} = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
post(conn, "/oauth/token", %{
|
|
||||||
grant_type: "client_credentials",
|
|
||||||
client_id: client_id,
|
|
||||||
client_secret: client_secret
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
|
||||||
json_response(conn, 200)
|
|
||||||
|
|
||||||
assert token
|
|
||||||
token_from_db = Repo.get_by(Token, token: token)
|
|
||||||
assert token_from_db
|
|
||||||
assert refresh
|
|
||||||
assert scope == "read write follow"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> put_req_header("authorization", "Bearer " <> token)
|
|
||||||
|> post("/api/v1/accounts", %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
bio: "Test Bio",
|
|
||||||
agreement: true,
|
|
||||||
reason: "I'm a cool dude, bro"
|
|
||||||
})
|
|
||||||
|
|
||||||
response = json_response_and_validate_schema(conn, 200)
|
|
||||||
assert %{"identifier" => "awaiting_approval"} = response
|
|
||||||
refute response["access_token"]
|
|
||||||
refute response["token_type"]
|
|
||||||
|
|
||||||
user = Repo.get_by(User, email: "lain@example.org")
|
|
||||||
|
|
||||||
assert user.approval_pending
|
|
||||||
assert user.registration_reason == "I'm a cool dude, bro"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
|
|
||||||
_user = insert(:user, email: "lain@example.org")
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> post("/api/v1/accounts", valid_params)
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 400) == %{
|
|
||||||
"error" => "{\"email\":[\"has already been taken\"]}"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns bad_request if missing required params", %{
|
|
||||||
conn: conn,
|
|
||||||
valid_params: valid_params
|
|
||||||
} do
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|
|
||||||
res = post(conn, "/api/v1/accounts", valid_params)
|
|
||||||
assert json_response_and_validate_schema(res, 200)
|
|
||||||
|
|
||||||
[{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
|
|
||||||
|> Stream.zip(Map.delete(valid_params, :email))
|
|
||||||
|> Enum.each(fn {ip, {attr, _}} ->
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> Map.put(:remote_ip, ip)
|
|
||||||
|> post("/api/v1/accounts", Map.delete(valid_params, attr))
|
|
||||||
|> json_response_and_validate_schema(400)
|
|
||||||
|
|
||||||
assert res == %{
|
|
||||||
"error" => "Missing field: #{attr}.",
|
|
||||||
"errors" => [
|
|
||||||
%{
|
|
||||||
"message" => "Missing field: #{attr}",
|
|
||||||
"source" => %{"pointer" => "/#{attr}"},
|
|
||||||
"title" => "Invalid value"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns bad_request if missing email params when :account_activation_required is enabled",
|
|
||||||
%{conn: conn, valid_params: valid_params} do
|
|
||||||
clear_config([:instance, :account_activation_required], true)
|
|
||||||
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> Map.put(:remote_ip, {127, 0, 0, 5})
|
|
||||||
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 400) ==
|
|
||||||
%{"error" => "Missing parameter: email"}
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> Map.put(:remote_ip, {127, 0, 0, 6})
|
|
||||||
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 400) == %{
|
|
||||||
"error" => "{\"email\":[\"can't be blank\"]}"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> Map.put(:remote_ip, {127, 0, 0, 7})
|
|
||||||
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 200)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
|
|
||||||
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> put_req_header("content-type", "application/json")
|
|
||||||
|> Map.put(:remote_ip, {127, 0, 0, 8})
|
|
||||||
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 200)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
|
|
||||||
res =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> "invalid-token")
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> post("/api/v1/accounts", valid_params)
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "registration from trusted app" do
|
|
||||||
clear_config([Pleroma.Captcha, :enabled], true)
|
|
||||||
app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
|
|
||||||
|
|
||||||
conn =
|
|
||||||
build_conn()
|
|
||||||
|> post("/oauth/token", %{
|
|
||||||
"grant_type" => "client_credentials",
|
|
||||||
"client_id" => app.client_id,
|
|
||||||
"client_secret" => app.client_secret
|
|
||||||
})
|
|
||||||
|
|
||||||
assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
|
|
||||||
|
|
||||||
response =
|
|
||||||
build_conn()
|
|
||||||
|> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|> post("/api/v1/accounts", %{
|
|
||||||
nickname: "nickanme",
|
|
||||||
agreement: true,
|
|
||||||
email: "email@example.com",
|
|
||||||
fullname: "Lain",
|
|
||||||
username: "Lain",
|
|
||||||
password: "some_password",
|
|
||||||
confirm: "some_password"
|
|
||||||
})
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"access_token" => access_token,
|
|
||||||
"created_at" => _,
|
|
||||||
"scope" => "read write follow push",
|
|
||||||
"token_type" => "Bearer"
|
|
||||||
} = response
|
|
||||||
|
|
||||||
response =
|
|
||||||
build_conn()
|
|
||||||
|> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
|
|
||||||
|> get("/api/v1/accounts/verify_credentials")
|
|
||||||
|> json_response_and_validate_schema(200)
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"acct" => "Lain",
|
|
||||||
"bot" => false,
|
|
||||||
"display_name" => "Lain",
|
|
||||||
"follow_requests_count" => 0,
|
|
||||||
"followers_count" => 0,
|
|
||||||
"following_count" => 0,
|
|
||||||
"locked" => false,
|
|
||||||
"note" => "",
|
|
||||||
"source" => %{
|
|
||||||
"fields" => [],
|
|
||||||
"note" => "",
|
|
||||||
"pleroma" => %{
|
|
||||||
"actor_type" => "Person",
|
|
||||||
"discoverable" => false,
|
|
||||||
"no_rich_text" => false,
|
|
||||||
"show_role" => true
|
|
||||||
},
|
|
||||||
"privacy" => "public",
|
|
||||||
"sensitive" => false
|
|
||||||
},
|
|
||||||
"statuses_count" => 0,
|
|
||||||
"username" => "Lain"
|
|
||||||
} = response
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "create account by app / rate limit" do
|
|
||||||
setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
|
|
||||||
|
|
||||||
test "respects rate limit setting", %{conn: conn} do
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
|
||||||
|> Map.put(:remote_ip, {15, 15, 15, 15})
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|
|
||||||
for i <- 1..2 do
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/accounts", %{
|
|
||||||
username: "#{i}lain",
|
|
||||||
email: "#{i}lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true
|
|
||||||
})
|
|
||||||
|
|
||||||
%{
|
|
||||||
"access_token" => token,
|
|
||||||
"created_at" => _created_at,
|
|
||||||
"scope" => _scope,
|
|
||||||
"token_type" => "Bearer"
|
|
||||||
} = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
|
||||||
token_from_db = Repo.get_by(Token, token: token)
|
|
||||||
assert token_from_db
|
|
||||||
token_from_db = Repo.preload(token_from_db, :user)
|
|
||||||
assert token_from_db.user
|
|
||||||
end
|
|
||||||
|
|
||||||
conn =
|
|
||||||
post(conn, "/api/v1/accounts", %{
|
|
||||||
username: "6lain",
|
|
||||||
email: "6lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true
|
|
||||||
})
|
|
||||||
|
|
||||||
assert json_response_and_validate_schema(conn, :too_many_requests) == %{
|
|
||||||
"error" => "Throttled"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "create account with enabled captcha" do
|
|
||||||
setup %{conn: conn} do
|
|
||||||
app_token = insert(:oauth_token, user: nil)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|
||||||
|
|
||||||
[conn: conn]
|
|
||||||
end
|
|
||||||
|
|
||||||
setup do: clear_config([Pleroma.Captcha, :enabled], true)
|
|
||||||
|
|
||||||
test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
|
|
||||||
%{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
|
|
||||||
|
|
||||||
params = %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true,
|
|
||||||
captcha_solution: Pleroma.Captcha.Mock.solution(),
|
|
||||||
captcha_token: token,
|
|
||||||
captcha_answer_data: answer_data
|
|
||||||
}
|
|
||||||
|
|
||||||
assert %{
|
|
||||||
"access_token" => access_token,
|
|
||||||
"created_at" => _,
|
|
||||||
"scope" => "read",
|
|
||||||
"token_type" => "Bearer"
|
|
||||||
} =
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/accounts", params)
|
|
||||||
|> json_response_and_validate_schema(:ok)
|
|
||||||
|
|
||||||
assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
|
|
||||||
|
|
||||||
Cachex.del(:used_captcha_cache, token)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns 400 if any captcha field is not provided", %{conn: conn} do
|
|
||||||
captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
|
|
||||||
|
|
||||||
valid_params = %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true,
|
|
||||||
captcha_solution: "xx",
|
|
||||||
captcha_token: "xx",
|
|
||||||
captcha_answer_data: "xx"
|
|
||||||
}
|
|
||||||
|
|
||||||
for field <- captcha_fields do
|
|
||||||
expected = %{
|
|
||||||
"error" => "{\"captcha\":[\"Invalid CAPTCHA (Missing parameter: #{field})\"]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert expected ==
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/accounts", Map.delete(valid_params, field))
|
|
||||||
|> json_response_and_validate_schema(:bad_request)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns an error if captcha is invalid", %{conn: conn} do
|
|
||||||
params = %{
|
|
||||||
username: "lain",
|
|
||||||
email: "lain@example.org",
|
|
||||||
password: "PlzDontHackLain",
|
|
||||||
agreement: true,
|
|
||||||
captcha_solution: "cofe",
|
|
||||||
captcha_token: "cofe",
|
|
||||||
captcha_answer_data: "cofe"
|
|
||||||
}
|
|
||||||
|
|
||||||
assert %{"error" => "{\"captcha\":[\"Invalid answer data\"]}"} ==
|
|
||||||
conn
|
|
||||||
|> post("/api/v1/accounts", params)
|
|
||||||
|> json_response_and_validate_schema(:bad_request)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "GET /api/v1/accounts/:id/lists - account_lists" do
|
describe "GET /api/v1/accounts/:id/lists - account_lists" do
|
||||||
test "returns lists to which the account belongs" do
|
test "returns lists to which the account belongs" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:lists"])
|
%{user: user, conn: conn} = oauth_access(["read:lists"])
|
||||||
|
691
test/pleroma/web/mastodon_api/registration_user_test.exs
Normal file
691
test/pleroma/web/mastodon_api/registration_user_test.exs
Normal file
@ -0,0 +1,691 @@
|
|||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.RegistrationUserTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "create account by app" do
|
||||||
|
setup do
|
||||||
|
valid_params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true
|
||||||
|
}
|
||||||
|
|
||||||
|
[valid_params: valid_params]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registers and logs in without :account_activation_required / :account_approval_required",
|
||||||
|
%{conn: conn} do
|
||||||
|
clear_config([:instance, :account_activation_required], false)
|
||||||
|
clear_config([:instance, :account_approval_required], false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/apps", %{
|
||||||
|
client_name: "client_name",
|
||||||
|
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
scopes: "read, write, follow"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => client_id,
|
||||||
|
"client_secret" => client_secret,
|
||||||
|
"id" => _,
|
||||||
|
"name" => "client_name",
|
||||||
|
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"vapid_key" => _,
|
||||||
|
"website" => nil
|
||||||
|
} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token", %{
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: client_id,
|
||||||
|
client_secret: client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
||||||
|
json_response(conn, 200)
|
||||||
|
|
||||||
|
assert token
|
||||||
|
token_from_db = Repo.get_by(Token, token: token)
|
||||||
|
assert token_from_db
|
||||||
|
assert refresh
|
||||||
|
assert scope == "read write follow"
|
||||||
|
|
||||||
|
clear_config([User, :email_blacklist], ["example.org"])
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
bio: "Test Bio",
|
||||||
|
agreement: true
|
||||||
|
}
|
||||||
|
|
||||||
|
response =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert response == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"email" => ["Invalid email"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
|
||||||
|
Pleroma.Config.put([User, :email_blacklist], [])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|
||||||
|
%{
|
||||||
|
"access_token" => token,
|
||||||
|
"created_at" => _created_at,
|
||||||
|
"scope" => ^scope,
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
token_from_db = Repo.get_by(Token, token: token)
|
||||||
|
assert token_from_db
|
||||||
|
user = Repo.preload(token_from_db, :user).user
|
||||||
|
|
||||||
|
assert user
|
||||||
|
refute user.confirmation_pending
|
||||||
|
refute user.approval_pending
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registers but does not log in with :account_activation_required", %{conn: conn} do
|
||||||
|
clear_config([:instance, :account_activation_required], true)
|
||||||
|
clear_config([:instance, :account_approval_required], false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/apps", %{
|
||||||
|
client_name: "client_name",
|
||||||
|
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
scopes: "read, write, follow"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => client_id,
|
||||||
|
"client_secret" => client_secret,
|
||||||
|
"id" => _,
|
||||||
|
"name" => "client_name",
|
||||||
|
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"vapid_key" => _,
|
||||||
|
"website" => nil
|
||||||
|
} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token", %{
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: client_id,
|
||||||
|
client_secret: client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
||||||
|
json_response(conn, 200)
|
||||||
|
|
||||||
|
assert token
|
||||||
|
token_from_db = Repo.get_by(Token, token: token)
|
||||||
|
assert token_from_db
|
||||||
|
assert refresh
|
||||||
|
assert scope == "read write follow"
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> post("/api/v1/accounts", %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
bio: "Test Bio",
|
||||||
|
agreement: true
|
||||||
|
})
|
||||||
|
|
||||||
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
assert %{"identifier" => "missing_confirmed_email"} = response
|
||||||
|
refute response["access_token"]
|
||||||
|
refute response["token_type"]
|
||||||
|
|
||||||
|
user = Repo.get_by(User, email: "lain@example.org")
|
||||||
|
assert user.confirmation_pending
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registers but does not log in with :account_approval_required", %{conn: conn} do
|
||||||
|
clear_config([:instance, :account_approval_required], true)
|
||||||
|
clear_config([:instance, :account_activation_required], false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/apps", %{
|
||||||
|
client_name: "client_name",
|
||||||
|
redirect_uris: "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
scopes: "read, write, follow"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"client_id" => client_id,
|
||||||
|
"client_secret" => client_secret,
|
||||||
|
"id" => _,
|
||||||
|
"name" => "client_name",
|
||||||
|
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"vapid_key" => _,
|
||||||
|
"website" => nil
|
||||||
|
} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/oauth/token", %{
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: client_id,
|
||||||
|
client_secret: client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token, "refresh_token" => refresh, "scope" => scope} =
|
||||||
|
json_response(conn, 200)
|
||||||
|
|
||||||
|
assert token
|
||||||
|
token_from_db = Repo.get_by(Token, token: token)
|
||||||
|
assert token_from_db
|
||||||
|
assert refresh
|
||||||
|
assert scope == "read write follow"
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> post("/api/v1/accounts", %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
bio: "Test Bio",
|
||||||
|
agreement: true,
|
||||||
|
reason: "I'm a cool dude, bro"
|
||||||
|
})
|
||||||
|
|
||||||
|
response = json_response_and_validate_schema(conn, 200)
|
||||||
|
assert %{"identifier" => "awaiting_approval"} = response
|
||||||
|
refute response["access_token"]
|
||||||
|
refute response["token_type"]
|
||||||
|
|
||||||
|
user = Repo.get_by(User, email: "lain@example.org")
|
||||||
|
|
||||||
|
assert user.approval_pending
|
||||||
|
assert user.registration_reason == "I'm a cool dude, bro"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error when user already registred", %{conn: conn, valid_params: valid_params} do
|
||||||
|
_user = insert(:user, email: "lain@example.org")
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/v1/accounts", valid_params)
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"email" => ["has already been taken"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns bad_request if missing required params", %{
|
||||||
|
conn: conn,
|
||||||
|
valid_params: valid_params
|
||||||
|
} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|
||||||
|
res = post(conn, "/api/v1/accounts", valid_params)
|
||||||
|
assert json_response_and_validate_schema(res, 200)
|
||||||
|
|
||||||
|
[{127, 0, 0, 1}, {127, 0, 0, 2}, {127, 0, 0, 3}, {127, 0, 0, 4}]
|
||||||
|
|> Stream.zip(Map.delete(valid_params, :email))
|
||||||
|
|> Enum.each(fn {ip, {attr, _}} ->
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> Map.put(:remote_ip, ip)
|
||||||
|
|> post("/api/v1/accounts", Map.delete(valid_params, attr))
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"#{attr}" => ["Missing field: #{attr}."]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns bad_request if missing email params when :account_activation_required is enabled",
|
||||||
|
%{conn: conn, valid_params: valid_params} do
|
||||||
|
clear_config([:instance, :account_activation_required], true)
|
||||||
|
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> Map.put(:remote_ip, {127, 0, 0, 5})
|
||||||
|
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"email" => ["Missing parameter: email"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> Map.put(:remote_ip, {127, 0, 0, 6})
|
||||||
|
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"email" => ["can't be blank"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allow registration without an email", %{conn: conn, valid_params: valid_params} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> Map.put(:remote_ip, {127, 0, 0, 7})
|
||||||
|
|> post("/api/v1/accounts", Map.delete(valid_params, :email))
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(res, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allow registration with an empty email", %{conn: conn, valid_params: valid_params} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
conn = put_req_header(conn, "authorization", "Bearer " <> app_token.token)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> Map.put(:remote_ip, {127, 0, 0, 8})
|
||||||
|
|> post("/api/v1/accounts", Map.put(valid_params, :email, ""))
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(res, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns forbidden if token is invalid", %{conn: conn, valid_params: valid_params} do
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> "invalid-token")
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/v1/accounts", valid_params)
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(res, 403) == %{"error" => "Invalid credentials"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "registration from trusted app" do
|
||||||
|
clear_config([Pleroma.Captcha, :enabled], true)
|
||||||
|
app = insert(:oauth_app, trusted: true, scopes: ["read", "write", "follow", "push"])
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> post("/oauth/token", %{
|
||||||
|
"grant_type" => "client_credentials",
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"client_secret" => app.client_secret
|
||||||
|
})
|
||||||
|
|
||||||
|
assert %{"access_token" => token, "token_type" => "Bearer"} = json_response(conn, 200)
|
||||||
|
|
||||||
|
response =
|
||||||
|
build_conn()
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer " <> token)
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|> post("/api/v1/accounts", %{
|
||||||
|
nickname: "nickanme",
|
||||||
|
agreement: true,
|
||||||
|
email: "email@example.com",
|
||||||
|
fullname: "Lain",
|
||||||
|
username: "Lain",
|
||||||
|
password: "some_password",
|
||||||
|
confirm: "some_password"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => access_token,
|
||||||
|
"created_at" => _,
|
||||||
|
"scope" => "read write follow push",
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} = response
|
||||||
|
|
||||||
|
response =
|
||||||
|
build_conn()
|
||||||
|
|> Plug.Conn.put_req_header("authorization", "Bearer " <> access_token)
|
||||||
|
|> get("/api/v1/accounts/verify_credentials")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"acct" => "Lain",
|
||||||
|
"bot" => false,
|
||||||
|
"display_name" => "Lain",
|
||||||
|
"follow_requests_count" => 0,
|
||||||
|
"followers_count" => 0,
|
||||||
|
"following_count" => 0,
|
||||||
|
"locked" => false,
|
||||||
|
"note" => "",
|
||||||
|
"source" => %{
|
||||||
|
"fields" => [],
|
||||||
|
"note" => "",
|
||||||
|
"pleroma" => %{
|
||||||
|
"actor_type" => "Person",
|
||||||
|
"discoverable" => false,
|
||||||
|
"no_rich_text" => false,
|
||||||
|
"show_role" => true
|
||||||
|
},
|
||||||
|
"privacy" => "public",
|
||||||
|
"sensitive" => false
|
||||||
|
},
|
||||||
|
"statuses_count" => 0,
|
||||||
|
"username" => "Lain"
|
||||||
|
} = response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "create account by app / rate limit" do
|
||||||
|
setup do: clear_config([:rate_limit, :app_account_creation], {10_000, 2})
|
||||||
|
|
||||||
|
test "respects rate limit setting", %{conn: conn} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> Map.put(:remote_ip, {15, 15, 15, 15})
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|
||||||
|
for i <- 1..2 do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", %{
|
||||||
|
username: "#{i}lain",
|
||||||
|
email: "#{i}lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true
|
||||||
|
})
|
||||||
|
|
||||||
|
%{
|
||||||
|
"access_token" => token,
|
||||||
|
"created_at" => _created_at,
|
||||||
|
"scope" => _scope,
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} = json_response_and_validate_schema(conn, 200)
|
||||||
|
|
||||||
|
token_from_db = Repo.get_by(Token, token: token)
|
||||||
|
assert token_from_db
|
||||||
|
token_from_db = Repo.preload(token_from_db, :user)
|
||||||
|
assert token_from_db.user
|
||||||
|
end
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/api/v1/accounts", %{
|
||||||
|
username: "6lain",
|
||||||
|
email: "6lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response_and_validate_schema(conn, :too_many_requests) == %{
|
||||||
|
"error" => "Throttled"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "create account via invite" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|
||||||
|
[conn: conn]
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do: clear_config([:instance, :registrations_open], false)
|
||||||
|
setup do: clear_config([Pleroma.Captcha, :enabled], false)
|
||||||
|
|
||||||
|
test "creates an account", %{conn: conn} do
|
||||||
|
invite = insert(:user_invite_token, %{invite_type: "one_time"})
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
token: invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
assert invite.used == true
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => access_token,
|
||||||
|
"created_at" => _,
|
||||||
|
"scope" => "read",
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} = res
|
||||||
|
|
||||||
|
user = Repo.get_by(Token, token: access_token) |> Repo.preload(:user) |> Map.get(:user)
|
||||||
|
assert user.email == "lain@example.org"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns error when already used", %{conn: conn} do
|
||||||
|
invite = insert(:user_invite_token, %{used: true, invite_type: "one_time"})
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
token: invite.token
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"invite" => ["Expired token"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns errors when invite is invalid", %{conn: conn} do
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
token: "fake-token"
|
||||||
|
}
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
|
||||||
|
assert res == %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"invite" => ["Invalid token"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "create account with enabled captcha" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|
||||||
|
[conn: conn]
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do: clear_config([Pleroma.Captcha, :enabled], true)
|
||||||
|
|
||||||
|
test "creates an account and returns 200 if captcha is valid", %{conn: conn} do
|
||||||
|
%{token: token, answer_data: answer_data} = Pleroma.Captcha.new()
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
captcha_solution: Pleroma.Captcha.Mock.solution(),
|
||||||
|
captcha_token: token,
|
||||||
|
captcha_answer_data: answer_data
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"access_token" => access_token,
|
||||||
|
"created_at" => _,
|
||||||
|
"scope" => "read",
|
||||||
|
"token_type" => "Bearer"
|
||||||
|
} =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert Token |> Repo.get_by(token: access_token) |> Repo.preload(:user) |> Map.get(:user)
|
||||||
|
|
||||||
|
Cachex.del(:used_captcha_cache, token)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 400 if any captcha field is not provided", %{conn: conn} do
|
||||||
|
captcha_fields = [:captcha_solution, :captcha_token, :captcha_answer_data]
|
||||||
|
|
||||||
|
valid_params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
captcha_solution: "xx",
|
||||||
|
captcha_token: "xx",
|
||||||
|
captcha_answer_data: "xx"
|
||||||
|
}
|
||||||
|
|
||||||
|
for field <- captcha_fields do
|
||||||
|
expected = %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"captcha" => ["Invalid CAPTCHA (Missing parameter: #{field})"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert expected ==
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", Map.delete(valid_params, field))
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns an error if captcha is invalid", %{conn: conn} do
|
||||||
|
params = %{
|
||||||
|
username: "lain",
|
||||||
|
email: "lain@example.org",
|
||||||
|
password: "PlzDontHackLain",
|
||||||
|
agreement: true,
|
||||||
|
captcha_solution: "cofe",
|
||||||
|
captcha_token: "cofe",
|
||||||
|
captcha_answer_data: "cofe"
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{"captcha" => ["Invalid answer data"]},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
} ==
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "api spec errors" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
app_token = insert(:oauth_token, user: nil)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer " <> app_token.token)
|
||||||
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|
|
||||||
|
[conn: conn]
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do: clear_config([:instance, :registrations_open], true)
|
||||||
|
setup do: clear_config([Pleroma.Captcha, :enabled], false)
|
||||||
|
|
||||||
|
test "returns errors when missed required field", %{conn: conn} do
|
||||||
|
params = %{
|
||||||
|
email: "lain@example.org",
|
||||||
|
agreement: true
|
||||||
|
}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"error" => "Please review the submission",
|
||||||
|
"fields" => %{
|
||||||
|
"password" => ["Missing field: password."],
|
||||||
|
"username" => ["Missing field: username."]
|
||||||
|
},
|
||||||
|
"identifier" => "review_submission"
|
||||||
|
} ==
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/accounts", params)
|
||||||
|
|> json_response_and_validate_schema(:bad_request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -183,7 +183,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Invalid token"
|
assert msg == %{invite: ["Invalid token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -203,7 +203,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -258,7 +258,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("vinny")
|
refute User.get_cached_by_nickname("vinny")
|
||||||
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
invite = Repo.get_by(UserInviteToken, token: invite.token)
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -363,7 +363,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -383,7 +383,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -405,7 +405,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, msg} = TwitterAPI.register_user(data)
|
{:error, msg} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert msg == "Expired token"
|
assert msg == %{invite: ["Expired token"]}
|
||||||
refute User.get_cached_by_nickname("GrimReaper")
|
refute User.get_cached_by_nickname("GrimReaper")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -420,7 +420,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||||||
|
|
||||||
{:error, error} = TwitterAPI.register_user(data)
|
{:error, error} = TwitterAPI.register_user(data)
|
||||||
|
|
||||||
assert is_binary(error)
|
assert error == %{
|
||||||
|
password: ["can't be blank"],
|
||||||
|
password_confirmation: ["can't be blank"]
|
||||||
|
}
|
||||||
|
|
||||||
refute User.get_cached_by_nickname("lain")
|
refute User.get_cached_by_nickname("lain")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -442,4 +442,13 @@ defmodule Pleroma.Factory do
|
|||||||
phrase: "cofe"
|
phrase: "cofe"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_invite_token_factory do
|
||||||
|
%Pleroma.UserInviteToken{
|
||||||
|
token: Base.url_encode64(:crypto.strong_rand_bytes(32)),
|
||||||
|
used: false,
|
||||||
|
invite_type: "one_time",
|
||||||
|
uses: 0
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user