Merge branch 'develop' into 'remove-twitter-api'

# Conflicts:
#   lib/pleroma/web/nodeinfo/nodeinfo_controller.ex
This commit is contained in:
lain 2020-05-16 17:07:09 +00:00
commit d15aa9d950
598 changed files with 28836 additions and 10625 deletions

View file

@ -8,10 +8,12 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
require Logger
alias Pleroma.Activity
alias Pleroma.MFA
alias Pleroma.Object.Fetcher
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.User
alias Pleroma.Web.Auth.Authenticator
alias Pleroma.Web.Auth.TOTPAuthenticator
alias Pleroma.Web.CommonAPI
@status_types ["Article", "Event", "Note", "Video", "Page", "Question"]
@ -68,6 +70,8 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
# POST /ostatus_subscribe
#
# adds a remote account in followers if user already is signed in.
#
def do_follow(%{assigns: %{user: %User{} = user}} = conn, %{"user" => %{"id" => id}}) do
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
@ -78,9 +82,33 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
end
end
# POST /ostatus_subscribe
#
# step 1.
# checks login\password and displays step 2 form of MFA if need.
#
def do_follow(conn, %{"authorization" => %{"name" => _, "password" => _, "id" => id}}) do
with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{_, {:ok, user}, _} <- {:auth, Authenticator.get_user(conn), followee},
{_, _, _, false} <- {:mfa_required, followee, user, MFA.require?(user)},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
redirect(conn, to: "/users/#{followee.id}")
else
error ->
handle_follow_error(conn, error)
end
end
# POST /ostatus_subscribe
#
# step 2
# checks TOTP code. otherwise displays form with errors
#
def do_follow(conn, %{"mfa" => %{"code" => code, "token" => token, "id" => id}}) do
with {_, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)},
{_, _, {:ok, %{user: user}}} <- {:mfa_token, followee, MFA.Token.validate(token)},
{_, _, _, {:ok, _}} <-
{:verify_mfa_code, followee, token, TOTPAuthenticator.verify(code, user)},
{:ok, _, _, _} <- CommonAPI.follow(user, followee) do
redirect(conn, to: "/users/#{followee.id}")
else
@ -94,6 +122,23 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."})
end
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
end
defp handle_follow_error(conn, {:verify_mfa_code, followee, token, _} = _) do
render(conn, "follow_mfa.html", %{
error: "Wrong authentication code",
followee: followee,
mfa_token: token
})
end
defp handle_follow_error(conn, {:mfa_required, followee, user, _} = _) do
{:ok, %{token: token}} = MFA.Token.create_token(user)
render(conn, "follow_mfa.html", %{followee: followee, mfa_token: token, error: false})
end
defp handle_follow_error(conn, {:auth, _, followee} = _) do
render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
end

View file

@ -24,13 +24,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(
@ -128,15 +121,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do
with lines <- String.split(list, "\n"),
followed_identifiers <-
Enum.map(lines, fn line ->
String.split(line, ",") |> List.first()
end)
|> List.delete("Account address") do
User.follow_import(follower, followed_identifiers)
json(conn, "job started")
end
followed_identifiers =
list
|> String.split("\n")
|> Enum.map(&(&1 |> String.split(",") |> List.first()))
|> List.delete("Account address")
|> Enum.map(&(&1 |> String.trim() |> String.trim_leading("@")))
|> Enum.reject(&(&1 == ""))
User.follow_import(follower, followed_identifiers)
json(conn, "job started")
end
def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
@ -144,10 +138,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do
with blocked_identifiers <- String.split(list) do
User.blocks_import(blocker, blocked_identifiers)
json(conn, "job started")
end
blocked_identifiers = list |> String.split() |> Enum.map(&String.trim_leading(&1, "@"))
User.blocks_import(blocker, blocked_identifiers)
json(conn, "job started")
end
def change_password(%{assigns: %{user: user}} = conn, params) do

View file

@ -3,81 +3,39 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
import Pleroma.Web.Gettext
alias Pleroma.Emails.Mailer
alias Pleroma.Emails.UserEmail
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.UserInviteToken
require Pleroma.Constants
def register_user(params, opts \\ []) do
token = params["token"]
params =
params
|> Map.take([:email, :token, :password])
|> Map.put(:bio, params |> Map.get(:bio, "") |> User.parse_bio())
|> Map.put(:nickname, params[:username])
|> Map.put(:name, Map.get(params, :fullname, params[:username]))
|> Map.put(:password_confirmation, params[:password])
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"]
}
captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled])
# true if captcha is disabled or enabled and valid, false otherwise
captcha_ok =
if not 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]})}}
if Pleroma.Config.get([:instance, :registrations_open]) do
create_user(params, opts)
else
registration_process(
params,
%{
registrations_open: Pleroma.Config.get([:instance, :registrations_open]),
token: token
},
opts
)
create_user_with_invite(params, opts)
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? ->
UserInviteToken.update_usage!(invite)
create_user(params, opts)
_ ->
{:error, "Expired token"}
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)
else
nil -> {:error, "Invalid token"}
_ -> {:error, "Expired token"}
end
end
@ -90,16 +48,17 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
{:error, changeset} ->
errors =
Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
changeset
|> Ecto.Changeset.traverse_errors(fn {msg, _opts} -> msg end)
|> Jason.encode!()
{:error, %{error: errors}}
{:error, errors}
end
end
def password_reset(nickname_or_email) do
with true <- is_binary(nickname_or_email),
%User{local: true, email: email} = user when not is_nil(email) <-
%User{local: true, email: email} = user when is_binary(email) <-
User.get_by_nickname_or_email(nickname_or_email),
{:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do
user
@ -121,4 +80,58 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
{:error, "unknown user"}
end
end
def validate_captcha(app, params) do
if app.trusted || not Pleroma.Captcha.enabled?() do
:ok
else
do_validate_captcha(params)
end
end
defp do_validate_captcha(params) do
with :ok <- validate_captcha_presence(params),
:ok <-
Pleroma.Captcha.validate(
params[:captcha_token],
params[:captcha_solution],
params[:captcha_answer_data]
) do
:ok
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} ->
captcha_error(error)
end
end
defp validate_captcha_presence(params) do
[:captcha_solution, :captcha_token, :captcha_answer_data]
|> Enum.find_value(:ok, fn key ->
unless is_binary(params[key]) do
error = dgettext("errors", "Invalid CAPTCHA (Missing parameter: %{name})", name: key)
{:error, error}
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

View file

@ -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,9 +14,17 @@ 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(Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug)
plug(
:skip_plug,
[OAuthScopesPlug, EnsurePublicOrAuthenticatedPlug] when action == :confirm_email
)
plug(:skip_plug, OAuthScopesPlug when action in [:oauth_tokens, :revoke_token])
action_fallback(:errors)
@ -44,13 +53,13 @@ defmodule Pleroma.Web.TwitterAPI.Controller 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")
@ -62,7 +71,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller 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)
@ -73,7 +85,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|> 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