@@ -105,6 +105,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). | |||
- ActivityPub: Optional signing of ActivityPub object fetches. | |||
- Admin API: Endpoint for fetching latest user's statuses | |||
- Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=<email>` for resending account confirmation. | |||
- Pleroma API: Email change endpoint. | |||
- Relays: Added a task to list relay subscriptions. | |||
- Mix Tasks: `mix pleroma.database fix_likes_collections` | |||
- Federation: Remove `likes` from objects. | |||
@@ -252,7 +252,7 @@ See [Admin-API](Admin-API.md) | |||
* Params: | |||
* `email`: email of that needs to be verified | |||
* Authentication: not required | |||
* Response: 204 No Content | |||
* Response: 204 No Content | |||
## `/api/v1/pleroma/mascot` | |||
### Gets user mascot image | |||
@@ -321,6 +321,15 @@ See [Admin-API](Admin-API.md) | |||
} | |||
``` | |||
## `/api/pleroma/change_email` | |||
### Change account email | |||
* Method `POST` | |||
* Authentication: required | |||
* Params: | |||
* `password`: user's password | |||
* `email`: new email | |||
* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise | |||
# Pleroma Conversations | |||
Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints: | |||
@@ -1624,4 +1624,13 @@ defmodule Pleroma.User do | |||
def is_internal_user?(%User{nickname: nil}), do: true | |||
def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true | |||
def is_internal_user?(_), do: false | |||
def change_email(user, email) do | |||
user | |||
|> cast(%{email: email}, [:email]) | |||
|> validate_required([:email]) | |||
|> unique_constraint(:email) | |||
|> validate_format(:email, @email_regex) | |||
|> update_and_set_cache() | |||
end | |||
end |
@@ -224,6 +224,7 @@ defmodule Pleroma.Web.Router do | |||
scope [] do | |||
pipe_through(:oauth_write) | |||
post("/change_email", UtilController, :change_email) | |||
post("/change_password", UtilController, :change_password) | |||
post("/delete_account", UtilController, :delete_account) | |||
put("/notification_settings", UtilController, :update_notificaton_settings) | |||
@@ -314,6 +314,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do | |||
end | |||
end | |||
def change_email(%{assigns: %{user: user}} = conn, params) do | |||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do | |||
{:ok, user} -> | |||
with {:ok, _user} <- User.change_email(user, params["email"]) do | |||
json(conn, %{status: "success"}) | |||
else | |||
{:error, changeset} -> | |||
{_, {error, _}} = Enum.at(changeset.errors, 0) | |||
json(conn, %{error: "Email #{error}."}) | |||
_ -> | |||
json(conn, %{error: "Unable to change email."}) | |||
end | |||
{:error, msg} -> | |||
json(conn, %{error: msg}) | |||
end | |||
end | |||
def delete_account(%{assigns: %{user: user}} = conn, params) do | |||
case CommonAPI.Utils.confirm_current_password(user, params["password"]) do | |||
{:ok, user} -> | |||
@@ -1614,4 +1614,31 @@ defmodule Pleroma.UserTest do | |||
assert User.user_info(other_user).following_count == 152 | |||
end | |||
end | |||
describe "change_email/2" do | |||
setup do | |||
[user: insert(:user)] | |||
end | |||
test "blank email returns error", %{user: user} do | |||
assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "") | |||
assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil) | |||
end | |||
test "non unique email returns error", %{user: user} do | |||
%{email: email} = insert(:user) | |||
assert {:error, %{errors: [email: {"has already been taken", _}]}} = | |||
User.change_email(user, email) | |||
end | |||
test "invalid email returns error", %{user: user} do | |||
assert {:error, %{errors: [email: {"has invalid format", _}]}} = | |||
User.change_email(user, "cofe") | |||
end | |||
test "changes email", %{user: user} do | |||
assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party") | |||
end | |||
end | |||
end |
@@ -662,4 +662,111 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do | |||
assert called(Pleroma.Captcha.new()) | |||
end | |||
end | |||
defp with_credentials(conn, username, password) do | |||
header_content = "Basic " <> Base.encode64("#{username}:#{password}") | |||
put_req_header(conn, "authorization", header_content) | |||
end | |||
defp valid_user(_context) do | |||
user = insert(:user) | |||
[user: user] | |||
end | |||
describe "POST /api/pleroma/change_email" do | |||
setup [:valid_user] | |||
test "without credentials", %{conn: conn} do | |||
conn = post(conn, "/api/pleroma/change_email") | |||
assert json_response(conn, 403) == %{"error" => "Invalid credentials."} | |||
end | |||
test "with credentials and invalid password", %{conn: conn, user: current_user} do | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "hi", | |||
"email" => "test@test.com" | |||
}) | |||
assert json_response(conn, 200) == %{"error" => "Invalid password."} | |||
end | |||
test "with credentials, valid password and invalid email", %{ | |||
conn: conn, | |||
user: current_user | |||
} do | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "test", | |||
"email" => "foobar" | |||
}) | |||
assert json_response(conn, 200) == %{"error" => "Email has invalid format."} | |||
end | |||
test "with credentials, valid password and no email", %{ | |||
conn: conn, | |||
user: current_user | |||
} do | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "test" | |||
}) | |||
assert json_response(conn, 200) == %{"error" => "Email can't be blank."} | |||
end | |||
test "with credentials, valid password and blank email", %{ | |||
conn: conn, | |||
user: current_user | |||
} do | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "test", | |||
"email" => "" | |||
}) | |||
assert json_response(conn, 200) == %{"error" => "Email can't be blank."} | |||
end | |||
test "with credentials, valid password and non unique email", %{ | |||
conn: conn, | |||
user: current_user | |||
} do | |||
user = insert(:user) | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "test", | |||
"email" => user.email | |||
}) | |||
assert json_response(conn, 200) == %{"error" => "Email has already been taken."} | |||
end | |||
test "with credentials, valid password and valid email", %{ | |||
conn: conn, | |||
user: current_user | |||
} do | |||
conn = | |||
conn | |||
|> with_credentials(current_user.nickname, "test") | |||
|> post("/api/pleroma/change_email", %{ | |||
"password" => "test", | |||
"email" => "cofe@foobar.com" | |||
}) | |||
assert json_response(conn, 200) == %{"status" => "success"} | |||
end | |||
end | |||
end |