mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-30 14:57:59 +01:00
add button for mods to unlock account (derpibooru/philomena#173)
This commit is contained in:
parent
dbfdd22ea9
commit
6915d2ed45
6 changed files with 80 additions and 7 deletions
|
@ -234,7 +234,7 @@ defmodule Philomena.Users do
|
||||||
If the token matches, the user is marked as unlocked
|
If the token matches, the user is marked as unlocked
|
||||||
and the token is deleted.
|
and the token is deleted.
|
||||||
"""
|
"""
|
||||||
def unlock_user(token) do
|
def unlock_user_by_token(token) do
|
||||||
with {:ok, query} <- UserToken.verify_email_token_query(token, "unlock"),
|
with {:ok, query} <- UserToken.verify_email_token_query(token, "unlock"),
|
||||||
%User{} = user <- Repo.one(query),
|
%User{} = user <- Repo.one(query),
|
||||||
{:ok, %{user: user}} <- Repo.transaction(unlock_user_multi(user)) do
|
{:ok, %{user: user}} <- Repo.transaction(unlock_user_multi(user)) do
|
||||||
|
@ -252,6 +252,15 @@ defmodule Philomena.Users do
|
||||||
|> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, ["unlock"]))
|
|> Ecto.Multi.delete_all(:tokens, UserToken.user_and_contexts_query(user, ["unlock"]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Unconditionally unlocks the given user.
|
||||||
|
"""
|
||||||
|
def unlock_user(user) do
|
||||||
|
user
|
||||||
|
|> User.unlock_changeset()
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Delivers the unlock instructions to the given user.
|
Delivers the unlock instructions to the given user.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule PhilomenaWeb.Admin.User.UnlockController do
|
||||||
|
use PhilomenaWeb, :controller
|
||||||
|
|
||||||
|
alias Philomena.Users.User
|
||||||
|
alias Philomena.Users
|
||||||
|
|
||||||
|
plug :verify_authorized
|
||||||
|
plug :load_resource, model: User, id_name: "user_id", id_field: "slug", persisted: true
|
||||||
|
|
||||||
|
def create(conn, _params) do
|
||||||
|
{:ok, user} = Users.unlock_user(conn.assigns.user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_flash(:info, "User was unlocked.")
|
||||||
|
|> redirect(to: Routes.profile_path(conn, :show, user))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp verify_authorized(conn, _opts) do
|
||||||
|
case Canada.Can.can?(conn.assigns.current_user, :index, User) do
|
||||||
|
true -> conn
|
||||||
|
_false -> PhilomenaWeb.NotAuthorizedPlug.call(conn)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -30,7 +30,7 @@ defmodule PhilomenaWeb.UnlockController do
|
||||||
# Do not log in the user after unlocking to avoid a
|
# Do not log in the user after unlocking to avoid a
|
||||||
# leaked token giving the user access to the account.
|
# leaked token giving the user access to the account.
|
||||||
def show(conn, %{"id" => token}) do
|
def show(conn, %{"id" => token}) do
|
||||||
case Users.unlock_user(token) do
|
case Users.unlock_user_by_token(token) do
|
||||||
{:ok, _} ->
|
{:ok, _} ->
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Account unlocked successfully. You may now log in.")
|
|> put_flash(:info, "Account unlocked successfully. You may now log in.")
|
||||||
|
|
|
@ -365,6 +365,7 @@ defmodule PhilomenaWeb.Router do
|
||||||
only: [:create, :delete],
|
only: [:create, :delete],
|
||||||
singleton: true
|
singleton: true
|
||||||
|
|
||||||
|
resources "/unlock", User.UnlockController, only: [:create], singleton: true
|
||||||
resources "/api_key", User.ApiKeyController, only: [:delete], singleton: true
|
resources "/api_key", User.ApiKeyController, only: [:delete], singleton: true
|
||||||
resources "/downvotes", User.DownvoteController, only: [:delete], singleton: true
|
resources "/downvotes", User.DownvoteController, only: [:delete], singleton: true
|
||||||
resources "/votes", User.VoteController, only: [:delete], singleton: true
|
resources "/votes", User.VoteController, only: [:delete], singleton: true
|
||||||
|
|
|
@ -33,6 +33,18 @@
|
||||||
|
|
||||||
br
|
br
|
||||||
|
|
||||||
|
= if @user.locked_at do
|
||||||
|
i.fas.fa-fw.fa-lock>
|
||||||
|
strong.comment_deleted>
|
||||||
|
' Account locked,
|
||||||
|
=> @user.failed_attempts
|
||||||
|
' failed login attempts
|
||||||
|
- else
|
||||||
|
i.fas.fa-fw.fa-unlock>
|
||||||
|
' Not currently locked
|
||||||
|
|
||||||
|
br
|
||||||
|
|
||||||
a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__options__toggle" title="Toggle Controls"
|
a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__options__toggle" title="Toggle Controls"
|
||||||
i.fa.fa-fw.fa-bars
|
i.fa.fa-fw.fa-bars
|
||||||
span Toggle Controls
|
span Toggle Controls
|
||||||
|
@ -87,7 +99,7 @@ a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__optio
|
||||||
= if @forced do
|
= if @forced do
|
||||||
li
|
li
|
||||||
= link to: Routes.admin_user_force_filter_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
= link to: Routes.admin_user_force_filter_path(@conn, :delete, @user), data: [confirm: "Are you really, really sure?", method: "delete"] do
|
||||||
i.fas.faw-fw.fa-filter
|
i.fas.fa-fw.fa-filter
|
||||||
span.admin__button Remove Force Filter
|
span.admin__button Remove Force Filter
|
||||||
|
|
||||||
= if @user.deleted_at do
|
= if @user.deleted_at do
|
||||||
|
@ -101,6 +113,12 @@ a.label.label--primary.label--block href="#" data-click-toggle=".js-admin__optio
|
||||||
i.fa.fa-fw.fa-times
|
i.fa.fa-fw.fa-times
|
||||||
span.admin__button Deactivate Account
|
span.admin__button Deactivate Account
|
||||||
|
|
||||||
|
= if @user.locked_at do
|
||||||
|
li
|
||||||
|
= link to: Routes.admin_user_unlock_path(@conn, :create, @user), data: [method: "post"] do
|
||||||
|
i.fas.fa-fw.fa-unlock
|
||||||
|
span.admin__button Unlock Account
|
||||||
|
|
||||||
li
|
li
|
||||||
= link to: Routes.admin_user_wipe_path(@conn, :create, @user), data: [confirm: "This is irreversible, destroying all identifying information including email. Are you sure?", method: "post"] do
|
= link to: Routes.admin_user_wipe_path(@conn, :create, @user), data: [confirm: "This is irreversible, destroying all identifying information including email. Are you sure?", method: "post"] do
|
||||||
i.fas.fa-fw.fa-eraser
|
i.fas.fa-fw.fa-eraser
|
||||||
|
|
|
@ -501,7 +501,7 @@ defmodule Philomena.UsersTest do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "unlock_user/2" do
|
describe "unlock_user_by_token/1" do
|
||||||
setup do
|
setup do
|
||||||
user = locked_user_fixture()
|
user = locked_user_fixture()
|
||||||
|
|
||||||
|
@ -514,26 +514,47 @@ defmodule Philomena.UsersTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "unlocks the user with a valid token", %{user: user, token: token} do
|
test "unlocks the user with a valid token", %{user: user, token: token} do
|
||||||
assert {:ok, unlocked_user} = Users.unlock_user(token)
|
assert {:ok, unlocked_user} = Users.unlock_user_by_token(token)
|
||||||
refute unlocked_user.locked_at
|
refute unlocked_user.locked_at
|
||||||
refute Repo.get!(User, user.id).locked_at
|
refute Repo.get!(User, user.id).locked_at
|
||||||
refute Repo.get_by(UserToken, user_id: user.id)
|
refute Repo.get_by(UserToken, user_id: user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not confirm with invalid token", %{user: user} do
|
test "does not confirm with invalid token", %{user: user} do
|
||||||
assert Users.unlock_user("oops") == :error
|
assert Users.unlock_user_by_token("oops") == :error
|
||||||
assert Repo.get!(User, user.id).locked_at
|
assert Repo.get!(User, user.id).locked_at
|
||||||
assert Repo.get_by(UserToken, user_id: user.id)
|
assert Repo.get_by(UserToken, user_id: user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "does not unlocked if token expired", %{user: user, token: token} do
|
test "does not unlocked if token expired", %{user: user, token: token} do
|
||||||
{1, nil} = Repo.update_all(UserToken, set: [created_at: ~N[2020-01-01 00:00:00]])
|
{1, nil} = Repo.update_all(UserToken, set: [created_at: ~N[2020-01-01 00:00:00]])
|
||||||
assert Users.unlock_user(token) == :error
|
assert Users.unlock_user_by_token(token) == :error
|
||||||
assert Repo.get!(User, user.id).locked_at
|
assert Repo.get!(User, user.id).locked_at
|
||||||
assert Repo.get_by(UserToken, user_id: user.id)
|
assert Repo.get_by(UserToken, user_id: user.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "unlock_user/1" do
|
||||||
|
setup do
|
||||||
|
user = user_fixture()
|
||||||
|
locked_user = locked_user_fixture()
|
||||||
|
|
||||||
|
%{user: user, locked_user: locked_user}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "unlocks the user when locked", %{locked_user: locked_user} do
|
||||||
|
assert {:ok, unlocked_user} = Users.unlock_user(locked_user)
|
||||||
|
refute unlocked_user.locked_at
|
||||||
|
refute Repo.get!(User, unlocked_user.id).locked_at
|
||||||
|
end
|
||||||
|
|
||||||
|
test "does nothing when not locked", %{user: user} do
|
||||||
|
assert {:ok, unlocked_user} = Users.unlock_user(user)
|
||||||
|
refute unlocked_user.locked_at
|
||||||
|
refute Repo.get!(User, unlocked_user.id).locked_at
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "deliver_user_reset_password_instructions/2" do
|
describe "deliver_user_reset_password_instructions/2" do
|
||||||
setup do
|
setup do
|
||||||
%{user: user_fixture()}
|
%{user: user_fixture()}
|
||||||
|
|
Loading…
Reference in a new issue