From 019ce80fc529a0437984fdc3d1ab6916f34dd594 Mon Sep 17 00:00:00 2001
From: Oleg Lobanov <oleg@lobanov.me>
Date: Mon, 11 Jan 2021 22:33:36 +0100
Subject: [PATCH] fix: don't allow to remove root user

---
 errors/errors.go                     |  1 +
 frontend/src/utils/vue.js            |  4 ++--
 frontend/src/views/settings/User.vue |  2 +-
 http/users.go                        |  4 ++--
 http/utils.go                        |  2 ++
 users/storage.go                     | 20 ++++++++++++++------
 6 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/errors/errors.go b/errors/errors.go
index c79e3783..5ec364c0 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -17,4 +17,5 @@ var (
 	ErrPermissionDenied     = errors.New("permission denied")
 	ErrInvalidRequestParams = errors.New("invalid request params")
 	ErrSourceIsParent       = errors.New("source is parent")
+	ErrRootUserDeletion     = errors.New("user with id 1 can't be deleted")
 )
diff --git a/frontend/src/utils/vue.js b/frontend/src/utils/vue.js
index 6cfe52b9..313b60dd 100644
--- a/frontend/src/utils/vue.js
+++ b/frontend/src/utils/vue.js
@@ -26,14 +26,14 @@ Vue.prototype.$showSuccess = (message) => {
   })).show()
 }
 
-Vue.prototype.$showError = (error) => {
+Vue.prototype.$showError = (error, displayReport = true) => {
   let btns = [
     Noty.button(i18n.t('buttons.close'), '', function () {
       n.close()
     })
   ]
 
-  if (!disableExternal) {
+  if (!disableExternal && displayReport) {
     btns.unshift(Noty.button(i18n.t('buttons.reportIssue'), '', function () {
       window.open('https://github.com/filebrowser/filebrowser/issues/new/choose')
     }))
diff --git a/frontend/src/views/settings/User.vue b/frontend/src/views/settings/User.vue
index 01581d21..3cff063c 100644
--- a/frontend/src/views/settings/User.vue
+++ b/frontend/src/views/settings/User.vue
@@ -115,7 +115,7 @@ export default {
         this.$router.push({ path: '/settings/users' })
         this.$showSuccess(this.$t('settings.userDeleted'))
       } catch (e) {
-        this.$showError(e)
+         (e.message === "403") ? this.$showError(this.$t("errors.forbidden"), false) : this.$showError(e)
       }
     },
     async save (event) {
diff --git a/http/users.go b/http/users.go
index b03c9e09..4e29c46a 100644
--- a/http/users.go
+++ b/http/users.go
@@ -99,8 +99,8 @@ var userGetHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request
 
 var userDeleteHandler = withSelfOrAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
 	err := d.store.Users.Delete(d.raw.(uint))
-	if err == errors.ErrNotExist {
-		return http.StatusNotFound, err
+	if err != nil {
+		return errToStatus(err), err
 	}
 
 	return http.StatusOK, nil
diff --git a/http/utils.go b/http/utils.go
index a8eac8ae..ce605b36 100644
--- a/http/utils.go
+++ b/http/utils.go
@@ -40,6 +40,8 @@ func errToStatus(err error) int {
 		return http.StatusForbidden
 	case errors.Is(err, libErrors.ErrInvalidRequestParams):
 		return http.StatusBadRequest
+	case errors.Is(err, libErrors.ErrRootUserDeletion):
+		return http.StatusForbidden
 	default:
 		return http.StatusInternalServerError
 	}
diff --git a/users/storage.go b/users/storage.go
index f9b3aa80..76a8a200 100644
--- a/users/storage.go
+++ b/users/storage.go
@@ -92,17 +92,25 @@ func (s *Storage) Save(user *User) error {
 // Delete allows you to delete a user by its name or username. The provided
 // id must be a string for username lookup or a uint for id lookup. If id
 // is neither, a ErrInvalidDataType will be returned.
-func (s *Storage) Delete(id interface{}) (err error) {
+func (s *Storage) Delete(id interface{}) error {
 	switch id := id.(type) {
 	case string:
-		err = s.back.DeleteByUsername(id)
+		user, err := s.back.GetBy(id)
+		if err != nil {
+			return err
+		}
+		if user.ID == 1 {
+			return errors.ErrRootUserDeletion
+		}
+		return s.back.DeleteByUsername(id)
 	case uint:
-		err = s.back.DeleteByID(id)
+		if id == 1 {
+			return errors.ErrRootUserDeletion
+		}
+		return s.back.DeleteByID(id)
 	default:
-		err = errors.ErrInvalidDataType
+		return errors.ErrInvalidDataType
 	}
-
-	return
 }
 
 // LastUpdate gets the timestamp for the last update of an user.