Ver código fonte

prevent self-role-change for non-superadmins and last superadmin

- Non-superadmin users cannot change their own role at all
- Superadmin cannot change their own role if they are the only one left
neyrogovnarik 1 mês atrás
pai
commit
966f05778d

+ 18 - 0
backend/internal/handlers/users.go

@@ -97,6 +97,9 @@ type adminUpdateUserRequest struct {
 func (h *UserHandler) AdminUpdateUser(w http.ResponseWriter, r *http.Request) {
 	id := chi.URLParam(r, "id")
 
+	currentUserID := middleware.GetUserID(r.Context())
+	currentUserRole := middleware.GetUserRole(r.Context())
+
 	r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize)
 	var req adminUpdateUserRequest
 	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -110,6 +113,21 @@ func (h *UserHandler) AdminUpdateUser(w http.ResponseWriter, r *http.Request) {
 	}
 
 	if req.Role != nil {
+		if id == currentUserID {
+			if currentUserRole != "superadmin" {
+				writeError(w, http.StatusForbidden, "вы не можете сменить свою роль", nil)
+				return
+			}
+			count, err := h.userRepo.CountByRole(r.Context(), "superadmin")
+			if err != nil {
+				writeError(w, http.StatusInternalServerError, "failed to count superadmins", err)
+				return
+			}
+			if count <= 1 {
+				writeError(w, http.StatusForbidden, "нельзя сменить роль единственного суперадмина", nil)
+				return
+			}
+		}
 		if err := h.userRepo.UpdateRole(r.Context(), id, *req.Role); err != nil {
 			writeError(w, http.StatusInternalServerError, "failed to update role", err)
 			return

+ 11 - 3
backend/internal/repository/users.go

@@ -66,14 +66,22 @@ func (r *UserRepo) UpdateStatus(ctx context.Context, id, status string) error {
 }
 
 func (r *UserRepo) HasSuperadmin(ctx context.Context) (bool, error) {
+	count, err := r.CountByRole(ctx, "superadmin")
+	if err != nil {
+		return false, err
+	}
+	return count > 0, nil
+}
+
+func (r *UserRepo) CountByRole(ctx context.Context, role string) (int, error) {
 	var count int
 	err := r.pool.QueryRow(ctx,
-		`SELECT COUNT(*) FROM users WHERE role = 'superadmin' AND deleted_at IS NULL`,
+		`SELECT COUNT(*) FROM users WHERE role = $1 AND deleted_at IS NULL`, role,
 	).Scan(&count)
 	if err != nil {
-		return false, fmt.Errorf("has superadmin: %w", err)
+		return 0, fmt.Errorf("count by role: %w", err)
 	}
-	return count > 0, nil
+	return count, nil
 }
 
 func (r *UserRepo) List(ctx context.Context, filter models.UserFilter) ([]*models.User, error) {