From 085745c5e49aadff13edc4a88aa8c0887bdc787a Mon Sep 17 00:00:00 2001
From: Timothy Carambat <rambat1010@gmail.com>
Date: Tue, 14 Nov 2023 14:43:40 -0800
Subject: [PATCH] Prevent lone-admin from locking themselves out the system
 (#376)

resolves #367
---
 server/endpoints/admin.js           | 22 ++++++++++++++++++++++
 server/endpoints/api/admin/index.js | 22 ++++++++++++++++++++++
 server/models/user.js               |  2 +-
 3 files changed, 45 insertions(+), 1 deletion(-)

diff --git a/server/endpoints/admin.js b/server/endpoints/admin.js
index 26444f5a2..32fc35f9e 100644
--- a/server/endpoints/admin.js
+++ b/server/endpoints/admin.js
@@ -55,6 +55,28 @@ function adminEndpoints(app) {
       try {
         const { id } = request.params;
         const updates = reqBody(request);
+        const user = await User.get({ id: Number(id) });
+
+        // Check to make sure with this update that includes a role change to
+        // something other than admin that we still have at least one admin left.
+        if (
+          updates.hasOwnProperty("role") && // has admin prop to change
+          updates.role !== "admin" && // and we are changing to non-admin
+          user.role === "admin" // and they currently are an admin
+        ) {
+          const adminCount = await User.count({ role: "admin" });
+          if (adminCount - 1 <= 0) {
+            response
+              .status(200)
+              .json({
+                success: false,
+                error:
+                  "No system admins will remain if you do this. Update failed.",
+              });
+            return;
+          }
+        }
+
         const { success, error } = await User.update(id, updates);
         response.status(200).json({ success, error });
       } catch (e) {
diff --git a/server/endpoints/api/admin/index.js b/server/endpoints/api/admin/index.js
index ee147c304..0cba0ad6a 100644
--- a/server/endpoints/api/admin/index.js
+++ b/server/endpoints/api/admin/index.js
@@ -197,6 +197,28 @@ function apiAdminEndpoints(app) {
 
       const { id } = request.params;
       const updates = reqBody(request);
+      const user = await User.get({ id: Number(id) });
+
+      // Check to make sure with this update that includes a role change to
+      // something other than admin that we still have at least one admin left.
+      if (
+        updates.hasOwnProperty("role") && // has admin prop to change
+        updates.role !== "admin" && // and we are changing to non-admin
+        user.role === "admin" // and they currently are an admin
+      ) {
+        const adminCount = await User.count({ role: "admin" });
+        if (adminCount - 1 <= 0) {
+          response
+            .status(200)
+            .json({
+              success: false,
+              error:
+                "No system admins will remain if you do this. Update failed.",
+            });
+          return;
+        }
+      }
+
       const { success, error } = await User.update(id, updates);
       response.status(200).json({ success, error });
     } catch (e) {
diff --git a/server/models/user.js b/server/models/user.js
index 613aa95ed..c6d6771b6 100644
--- a/server/models/user.js
+++ b/server/models/user.js
@@ -21,7 +21,7 @@ const User = {
 
   update: async function (userId, updates = {}) {
     try {
-      const updatedUser = await prisma.users.update({
+      await prisma.users.update({
         where: { id: parseInt(userId) },
         data: updates,
       });
-- 
GitLab