mirror of
https://github.com/philomena-dev/philomena.git
synced 2025-04-20 02:03:59 +02:00
Merge 5d6604b57b
into 78550a6322
This commit is contained in:
commit
33f8a254b3
16 changed files with 233 additions and 3 deletions
lib
test
philomena_web/controllers
support/fixtures
|
@ -479,6 +479,22 @@ defmodule Philomena.Users do
|
|||
UserNotifier.deliver_reset_password_instructions(user, reset_password_url_fun.(encoded_token))
|
||||
end
|
||||
|
||||
@doc ~S"""
|
||||
Delivers the reactivate account email to the given user.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> deliver_user_reactivation_instructions(user, &url(~p"/reactivations/#{&1}"))
|
||||
{:ok, %{to: ..., body: ...}}
|
||||
|
||||
"""
|
||||
def deliver_user_reactivation_instructions(%User{} = user, reactivation_url_fun)
|
||||
when is_function(reactivation_url_fun, 1) do
|
||||
{encoded_token, user_token} = UserToken.build_email_token(user, "reactivate")
|
||||
Repo.insert!(user_token)
|
||||
UserNotifier.deliver_reactivation_instructions(user, reactivation_url_fun.(encoded_token))
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the user by reset password token.
|
||||
|
||||
|
@ -780,7 +796,7 @@ defmodule Philomena.Users do
|
|||
end
|
||||
|
||||
@doc """
|
||||
Reactivates a previously deactivated user account.
|
||||
Reactivates a previously deactivated user account. Removes all "reactivate" user tokens for that user if they exist.
|
||||
|
||||
## Examples
|
||||
|
||||
|
@ -789,6 +805,8 @@ defmodule Philomena.Users do
|
|||
|
||||
"""
|
||||
def reactivate_user(%User{} = user) do
|
||||
UserToken.user_and_contexts_query(user, ["reactivate"]) |> Repo.delete_all()
|
||||
|
||||
user
|
||||
|> User.reactivate_changeset()
|
||||
|> Repo.update()
|
||||
|
@ -811,6 +829,42 @@ defmodule Philomena.Users do
|
|||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deactivates a user account with the user recorded performing the deactivation.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> deactivate_user(user)
|
||||
{:ok, %User{}}
|
||||
|
||||
"""
|
||||
def deactivate_user(%User{} = user) do
|
||||
user
|
||||
|> User.deactivate_changeset(user)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets the user by reactivation token.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_user_by_reactivation_token("validtoken")
|
||||
%User{}
|
||||
|
||||
iex> get_user_by_reactivation_token("invalidtoken")
|
||||
nil
|
||||
|
||||
"""
|
||||
def get_user_by_reactivation_token(token) do
|
||||
with {:ok, query} <- UserToken.verify_email_token_query(token, "reactivate"),
|
||||
%User{} = user <- Repo.one(query) do
|
||||
user
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Generates a new API key for the user.
|
||||
|
||||
|
|
|
@ -419,10 +419,10 @@ defmodule Philomena.Users.User do
|
|||
change(user, deleted_at: nil, deleted_by_user_id: nil)
|
||||
end
|
||||
|
||||
def deactivate_changeset(user, moderator) do
|
||||
def deactivate_changeset(user, deactivator) do
|
||||
now = DateTime.utc_now(:second)
|
||||
|
||||
change(user, deleted_at: now, deleted_by_user_id: moderator.id)
|
||||
change(user, deleted_at: now, deleted_by_user_id: deactivator.id)
|
||||
end
|
||||
|
||||
def api_key_changeset(user) do
|
||||
|
|
|
@ -52,6 +52,27 @@ defmodule Philomena.Users.UserNotifier do
|
|||
""")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deliver instructions to reactivate a deactivated account.
|
||||
"""
|
||||
def deliver_reactivation_instructions(user, url) do
|
||||
deliver(user.email, "Reactivation instructions for your account", """
|
||||
|
||||
==============================
|
||||
|
||||
Hi,
|
||||
|
||||
Your account has been deactivated. If you wish to re-activate it, please click the following link:
|
||||
|
||||
#{url}
|
||||
|
||||
Sincerely,
|
||||
The Derpibooru team.
|
||||
|
||||
==============================
|
||||
""")
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deliver instructions to update an account email.
|
||||
"""
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Philomena.Users.UserToken do
|
|||
@change_email_validity_in_days 7
|
||||
@unlock_email_validity_in_days 7
|
||||
@session_validity_in_days 365
|
||||
@reactivate_validity_in_days 365 * 100
|
||||
|
||||
schema "user_tokens" do
|
||||
field :token, :binary
|
||||
|
@ -121,6 +122,7 @@ defmodule Philomena.Users.UserToken do
|
|||
defp days_for_context("confirm"), do: @confirm_validity_in_days
|
||||
defp days_for_context("reset_password"), do: @reset_password_validity_in_days
|
||||
defp days_for_context("unlock"), do: @unlock_email_validity_in_days
|
||||
defp days_for_context("reactivate"), do: @reactivate_validity_in_days
|
||||
|
||||
@doc """
|
||||
Checks if the token is valid and returns its underlying lookup query.
|
||||
|
|
19
lib/philomena_web/controllers/deactivation_controller.ex
Normal file
19
lib/philomena_web/controllers/deactivation_controller.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule PhilomenaWeb.DeactivationController do
|
||||
use PhilomenaWeb, :controller
|
||||
alias PhilomenaWeb.UserAuth
|
||||
alias Philomena.Users
|
||||
|
||||
def show(conn, _params) do
|
||||
render(conn, "index.html", title: "Deactivate Account")
|
||||
end
|
||||
|
||||
def delete(conn, _params) do
|
||||
user = conn.assigns.current_user
|
||||
|
||||
Users.deactivate_user(user)
|
||||
Users.deliver_user_reactivation_instructions(user, &url(~p"/reactivations/#{&1}"))
|
||||
UserAuth.log_out_user(conn)
|
||||
|
||||
conn |> redirect(to: "/")
|
||||
end
|
||||
end
|
22
lib/philomena_web/controllers/reactivation_controller.ex
Normal file
22
lib/philomena_web/controllers/reactivation_controller.ex
Normal file
|
@ -0,0 +1,22 @@
|
|||
defmodule PhilomenaWeb.ReactivationController do
|
||||
use PhilomenaWeb, :controller
|
||||
alias Philomena.Users.{User}
|
||||
alias Philomena.Users
|
||||
|
||||
def show(conn, %{"id" => _}) do
|
||||
render(conn, "show.html", title: "Reactivate Your Account")
|
||||
end
|
||||
|
||||
def create(conn, %{"token" => token}) do
|
||||
with user = %User{} <- Users.get_user_by_reactivation_token(token) do
|
||||
Users.reactivate_user(user)
|
||||
else
|
||||
nil ->
|
||||
nil
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, "If the token provided was valid, your account has been reactivated.")
|
||||
|> redirect(to: "/")
|
||||
end
|
||||
end
|
|
@ -78,6 +78,7 @@ defmodule PhilomenaWeb.Router do
|
|||
:redirect_if_user_is_authenticated
|
||||
]
|
||||
|
||||
resources "/reactivations", ReactivationController, only: [:show, :create]
|
||||
resources "/registrations", RegistrationController, only: [:new, :create], singleton: true
|
||||
end
|
||||
|
||||
|
@ -103,6 +104,7 @@ defmodule PhilomenaWeb.Router do
|
|||
|
||||
resources "/registrations", RegistrationController, only: [:edit, :update], singleton: true
|
||||
resources "/sessions", SessionController, only: [:delete], singleton: true
|
||||
resources "/deactivations", DeactivationController, only: [:show, :delete], singleton: true
|
||||
|
||||
scope "/registrations", Registration, as: :registration do
|
||||
resources "/totp", TotpController, only: [:edit, :update], singleton: true
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
h1 Deactivate Account
|
||||
- contact_email = "ops@derpibooru.org"
|
||||
.block
|
||||
.block__content.warning
|
||||
p.walloftext
|
||||
' Deactivating your account will prevent you from logging into your account. If you ever wish to re-activate your account later on, you may do so at any time. If you wish to wipe your personally identifiable data as well, please contact <a href="mailto:#{contact_email}">#{contact_email}</a>
|
||||
.deactivations__button-container
|
||||
= button_to "Back", ~p"/registrations/edit", class: "button"
|
||||
= button_to "Confirm", ~p"/deactivations", class: ["button", "button--state-danger", "button--separate-left"], method: :delete, data: [confirm: "Are you sure you want to deactivate your account?"]
|
3
lib/philomena_web/templates/reactivation/show.html.slime
Normal file
3
lib/philomena_web/templates/reactivation/show.html.slime
Normal file
|
@ -0,0 +1,3 @@
|
|||
h1 Reactivate Your Account
|
||||
= button_to "Cancel", ~p"/", class: "button"
|
||||
= button_to "Reactivate", "", class: ["button", "button--state-success", "button--separate-left"], method: "post", data: [confirm: "Are you sure you want to reactivate your account?"]
|
|
@ -69,3 +69,8 @@ h3 Change password
|
|||
= error_tag f, :current_password
|
||||
|
||||
= submit "Change password", class: "button"
|
||||
|
||||
h3 Deactivate Account
|
||||
p
|
||||
' Navigate to the account deactivation page.
|
||||
= button_to "Deactivate account", ~p"/deactivations", class: "button"
|
3
lib/philomena_web/views/deactivation_view.ex
Normal file
3
lib/philomena_web/views/deactivation_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.DeactivationView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
3
lib/philomena_web/views/reactivation_view.ex
Normal file
3
lib/philomena_web/views/reactivation_view.ex
Normal file
|
@ -0,0 +1,3 @@
|
|||
defmodule PhilomenaWeb.ReactivationView do
|
||||
use PhilomenaWeb, :view
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
defmodule PhilomenaWeb.DeactivationControllerTest do
|
||||
use PhilomenaWeb.ConnCase
|
||||
|
||||
alias Philomena.Users
|
||||
|
||||
setup :register_and_log_in_user
|
||||
|
||||
describe "GET /deactivations" do
|
||||
test "renders the deactivate account page", %{conn: conn} do
|
||||
conn = get(conn, ~p"/deactivations")
|
||||
response = html_response(conn, 200)
|
||||
assert response =~ "<h1>Deactivate Account</h1>"
|
||||
end
|
||||
end
|
||||
|
||||
describe "DELETE /deactivations" do
|
||||
test "causes the user to be deactivated", %{conn: conn, user: user} do
|
||||
conn = delete(conn, ~p"/deactivations")
|
||||
assert redirected_to(conn) == ~p"/"
|
||||
conn = get(conn, ~p"/registrations/edit")
|
||||
assert redirected_to(conn) == ~p"/sessions/new"
|
||||
|
||||
user = Users.get_user!(user.id)
|
||||
assert user.deleted_by_user_id == user.id
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,41 @@
|
|||
defmodule PhilomenaWeb.ReactivationControllerTest do
|
||||
use PhilomenaWeb.ConnCase, async: true
|
||||
|
||||
alias Philomena.Repo
|
||||
alias Philomena.Users.UserToken
|
||||
alias Philomena.Users
|
||||
import Philomena.UsersFixtures
|
||||
|
||||
setup do
|
||||
%{user: deactivated_user_fixture()}
|
||||
end
|
||||
|
||||
describe "GET /reactivations/:id" do
|
||||
test "renders the reactivate account page", %{conn: conn} do
|
||||
conn = get(conn, ~p"/reactivations/pinkie-pie-is-best-pony")
|
||||
response = html_response(conn, 200)
|
||||
assert response =~ "<h1>Reactivate Your Account</h1>"
|
||||
end
|
||||
end
|
||||
|
||||
describe "POST /reactivations/" do
|
||||
test "reactivate account page works", %{conn: conn, user: user} do
|
||||
token =
|
||||
extract_user_token(fn url ->
|
||||
Users.deliver_user_reactivation_instructions(user, url)
|
||||
end)
|
||||
|
||||
assert UserToken.user_and_contexts_query(user, ["reactivate"]) |> Repo.exists?()
|
||||
|
||||
assert token != nil
|
||||
|
||||
conn = post(conn, ~p"/reactivations", %{"token" => token})
|
||||
assert redirected_to(conn) == ~p"/"
|
||||
|
||||
user = Users.get_user!(user.id)
|
||||
assert user.deleted_by_user_id == nil
|
||||
|
||||
assert not (UserToken.user_and_contexts_query(user, ["reactivate"]) |> Repo.exists?())
|
||||
end
|
||||
end
|
||||
end
|
|
@ -55,6 +55,12 @@ defmodule PhilomenaWeb.RegistrationControllerTest do
|
|||
assert response =~ "Settings"
|
||||
end
|
||||
|
||||
test "renders the deactivation section of the settings page", %{conn: conn} do
|
||||
conn = get(conn, ~p"/registrations/edit")
|
||||
response = html_response(conn, 200)
|
||||
assert response =~ "<h3>Deactivate Account</h3>"
|
||||
end
|
||||
|
||||
test "redirects if user is not logged in" do
|
||||
conn = build_conn()
|
||||
conn = get(conn, ~p"/registrations/edit")
|
||||
|
|
|
@ -37,6 +37,19 @@ defmodule Philomena.UsersFixtures do
|
|||
|> Repo.update!()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Fixture for a deactivated user.
|
||||
|
||||
If `deactivated_by_user` is `nil` the user will be deactivated by themselves.
|
||||
"""
|
||||
def deactivated_user_fixture(deactivated_by_user \\ nil, attrs \\ %{}) do
|
||||
user = user_fixture(attrs)
|
||||
|
||||
user
|
||||
|> Users.User.deactivate_changeset(deactivated_by_user || user)
|
||||
|> Repo.update!()
|
||||
end
|
||||
|
||||
def extract_user_token(fun) do
|
||||
{:ok, captured} = fun.(&"[TOKEN]#{&1}[TOKEN]")
|
||||
[_, token, _] = String.split(captured.text_body, "[TOKEN]")
|
||||
|
|
Loading…
Add table
Reference in a new issue