Prevent last admin from being deleted (#694)

* Prevent last admin from being deleted

* fix tests
This commit is contained in:
Po Chen
2022-06-10 12:57:39 +10:00
committed by GitHub
parent b5dc6b36fd
commit 4ac4818bed
5 changed files with 33 additions and 3 deletions

View File

@@ -32,6 +32,10 @@ defmodule FzHttp.Users do
Repo.exists?(from u in User, where: u.id == ^user_id)
end
def list_admins do
Repo.all(from User, where: [role: :admin])
end
def get_user!(email: email) do
Repo.get_by!(User, email: email)
end

View File

@@ -10,6 +10,12 @@ defmodule FzHttpWeb.UserController do
def delete(conn, _params) do
user = Authentication.get_current_user(conn)
with %{role: :admin} <- user do
unless length(Users.list_admins()) > 1 do
raise "Cannot delete one last admin"
end
end
case Users.delete_user(user) do
{:ok, _user} ->
FzHttpWeb.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})

View File

@@ -84,7 +84,7 @@
<%# This is purposefully a synchronous form in order to easily clear the session %>
<%= form_for @changeset, Routes.user_path(@socket, :delete), [id: "delete-account", method: :delete], fn _f -> %>
<%= submit(class: "button is-danger", data: [confirm: "Are you sure?"]) do %>
<%= submit(class: "button is-danger", data: [confirm: "Are you sure?"], disabled: !@allow_delete) do %>
<span class="icon is-small">
<i class="fas fa-trash"></i>
</span>

View File

@@ -25,7 +25,8 @@ defmodule FzHttpWeb.SettingLive.Account do
@impl Phoenix.LiveView
def handle_params(_params, _url, socket) do
{:noreply, socket}
admins = Users.list_admins()
{:noreply, assign(socket, :allow_delete, length(admins) > 1)}
end
@impl Phoenix.LiveView

View File

@@ -1,7 +1,11 @@
defmodule FzHttpWeb.UserControllerTest do
use FzHttpWeb.ConnCase, async: true
alias FzHttp.Users
alias FzHttp.{Users, UsersFixtures}
setup do
{:ok, extra_admin: UsersFixtures.user()}
end
describe "when user signed in" do
test "deletes the user", %{admin_conn: conn} do
@@ -9,9 +13,24 @@ defmodule FzHttpWeb.UserControllerTest do
assert redirected_to(test_conn) == Routes.root_path(test_conn, :index)
end
test "prevents deletion if no extra admin", %{admin_conn: conn, extra_admin: extra_admin} do
Users.delete_user(extra_admin)
assert_raise(RuntimeError, fn ->
delete(conn, Routes.user_path(conn, :delete))
end)
end
end
describe "when user is already deleted" do
setup do
# this allows there to be 2 admins left after the main test admin is
# deleted, so that the deletion doesn't raise
_yet_another_admin = UsersFixtures.user()
:ok
end
test "returns 404", %{admin_user: user, admin_conn: conn} do
user.id
|> Users.get_user!()