DEV: Allow plugins to mark user custom fields as editable only by staff

DEV: Allow plugins to mark user custom fields as editable only by staff

This adds a staff_only parameter to the register_editable_user_custom_field API. The default is false, to maintain backwards compatibility.

diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 629faaa..5ffc97b 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1425,7 +1425,8 @@ class UsersController < ApplicationController
       :card_background_upload_url
     ]
 
-    permitted << { custom_fields: User.editable_user_custom_fields } unless User.editable_user_custom_fields.blank?
+    editable_custom_fields = User.editable_user_custom_fields(by_staff: current_user.try(:staff?))
+    permitted << { custom_fields: editable_custom_fields } unless editable_custom_fields.blank?
     permitted.concat UserUpdater::OPTION_ATTR
     permitted.concat UserUpdater::CATEGORY_IDS.keys.map { |k| { k => [] } }
     permitted.concat UserUpdater::TAG_NAMES.keys
diff --git a/app/models/user.rb b/app/models/user.rb
index 04c6af2..af28a89 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -260,17 +260,31 @@ class User < ActiveRecord::Base
     @plugin_editable_user_custom_fields ||= {}
   end
 
-  def self.register_plugin_editable_user_custom_field(custom_field_name, plugin)
-    plugin_editable_user_custom_fields[custom_field_name] = plugin
+  def self.plugin_staff_editable_user_custom_fields
+    @plugin_staff_editable_user_custom_fields ||= {}
   end
 
-  def self.editable_user_custom_fields
+  def self.register_plugin_editable_user_custom_field(custom_field_name, plugin, staff_only: false)
+    if staff_only
+      plugin_staff_editable_user_custom_fields[custom_field_name] = plugin
+    else
+      plugin_editable_user_custom_fields[custom_field_name] = plugin
+    end
+  end
+
+  def self.editable_user_custom_fields(by_staff: false)
     fields = []
 
     plugin_editable_user_custom_fields.each do |k, v|
       fields << k if v.enabled?
     end
 
+    if by_staff
+      plugin_staff_editable_user_custom_fields.each do |k, v|
+        fields << k if v.enabled?
+      end
+    end
+
     fields.uniq
   end
 
diff --git a/lib/plugin/instance.rb b/lib/plugin/instance.rb
index 28a7c34..945def5 100644
--- a/lib/plugin/instance.rb
+++ b/lib/plugin/instance.rb
@@ -141,9 +141,9 @@ class Plugin::Instance
     end
   end
 
-  def register_editable_user_custom_field(field)
+  def register_editable_user_custom_field(field, staff_only: false)
     reloadable_patch do |plugin|
-      ::User.register_plugin_editable_user_custom_field(field, plugin) # plugin.enabled? is checked at runtime
+      ::User.register_plugin_editable_user_custom_field(field, plugin, staff_only: staff_only) # plugin.enabled? is checked at runtime
     end
   end
 
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index 9d4ab99..26a952f 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -1764,40 +1764,56 @@ describe UsersController do
             before do
               plugin = Plugin::Instance.new
               plugin.register_editable_user_custom_field :test2
+              plugin.register_editable_user_custom_field :test3, staff_only: true
             end
 
             after do
               User.plugin_editable_user_custom_fields.clear
+              User.plugin_staff_editable_user_custom_fields.clear
             end
 
             it "only updates allowed user fields" do
-              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2 } }
+              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2, test3: :hello3 } }
 
               expect(response.status).to eq(200)
               expect(user.custom_fields["test1"]).to be_blank
               expect(user.custom_fields["test2"]).to eq("hello2")
+              expect(user.custom_fields["test3"]).to be_blank
             end
 
             it "works alongside a user field" do
               user_field = Fabricate(:user_field, editable: true)
-              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2 }, user_fields: { user_field.id.to_s => 'happy' } }
+              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2, test3: :hello3 }, user_fields: { user_field.id.to_s => 'happy' } }
               expect(response.status).to eq(200)
               expect(user.custom_fields["test1"]).to be_blank
               expect(user.custom_fields["test2"]).to eq("hello2")
+              expect(user.custom_fields["test3"]).to eq(nil)
               expect(user.user_fields[user_field.id.to_s]).to eq('happy')
             end
 
             it "is secure when there are no registered editable fields" do
               User.plugin_editable_user_custom_fields.clear
-              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2 } }
+              User.plugin_staff_editable_user_custom_fields.clear
+              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2, test3: :hello3 } }
               expect(response.status).to eq(200)
               expect(user.custom_fields["test1"]).to be_blank
               expect(user.custom_fields["test2"]).to be_blank
+              expect(user.custom_fields["test3"]).to be_blank
 
               put "/u/#{user.username}.json", params: { custom_fields: ["arrayitem1", "arrayitem2"] }
               expect(response.status).to eq(200)
             end
 
+            it "allows staff to edit staff-editable fields" do
+              sign_in(Fabricate(:admin))
+              put "/u/#{user.username}.json", params: { custom_fields: { test1: :hello1, test2: :hello2, test3: :hello3 } }
+
+              expect(response.status).to eq(200)
+              expect(user.custom_fields["test1"]).to be_blank
+              expect(user.custom_fields["test2"]).to eq("hello2")
+              expect(user.custom_fields["test3"]).to eq("hello3")
+            end
+
           end
         end

GitHub sha: 67787799

1 Like