Browse Source

Use strong_parameters and drop protected_attributes

* Ensure admin attribute can not be set at sign up and the agents user_id is not changeable
* Remove ACCESSIBLE_ATTRIBUTES from the User model
* Remove side effect on agent_params from AgentsController#build_agent
Dominik Sander 8 years ago
parent
commit
b2f031003f

+ 0 - 1
Gemfile

@@ -80,7 +80,6 @@ unless Gem::Version.new(Bundler::VERSION) >= Gem::Version.new('1.5.0')
   exit 1
   exit 1
 end
 end
 
 
-gem 'protected_attributes', '~>1.0.8' # This must be loaded before some other gems, like delayed_job.
 gem 'ace-rails-ap', '~> 2.0.1'
 gem 'ace-rails-ap', '~> 2.0.1'
 gem 'bootstrap-kaminari-views', '~> 0.0.3'
 gem 'bootstrap-kaminari-views', '~> 0.0.3'
 gem 'bundler', '>= 1.5.0'
 gem 'bundler', '>= 1.5.0'

+ 0 - 3
Gemfile.lock

@@ -387,8 +387,6 @@ GEM
       multi_json (~> 1.0)
       multi_json (~> 1.0)
       websocket-driver (>= 0.2.0)
       websocket-driver (>= 0.2.0)
     polyglot (0.3.5)
     polyglot (0.3.5)
-    protected_attributes (1.0.8)
-      activemodel (>= 4.0.1, < 5.0)
     pry (0.10.3)
     pry (0.10.3)
       coderay (~> 1.1.0)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       method_source (~> 0.8.1)
@@ -656,7 +654,6 @@ DEPENDENCIES
   omniauth-wunderlist!
   omniauth-wunderlist!
   pg (~> 0.18.3)
   pg (~> 0.18.3)
   poltergeist
   poltergeist
-  protected_attributes (~> 1.0.8)
   pry-byebug
   pry-byebug
   pry-rails
   pry-rails
   quiet_assets
   quiet_assets

+ 0 - 1
app/concerns/oauthable.rb

@@ -3,7 +3,6 @@ module Oauthable
 
 
   included do |base|
   included do |base|
     @valid_oauth_providers = :all
     @valid_oauth_providers = :all
-    attr_accessible :service_id
     validates_presence_of :service_id
     validates_presence_of :service_id
   end
   end
 
 

+ 6 - 6
app/controllers/admin/users_controller.rb

@@ -19,10 +19,8 @@ class Admin::UsersController < ApplicationController
   end
   end
 
 
   def create
   def create
-    admin = params[:user].delete(:admin)
-    @user = User.new(params[:user])
+    @user = User.new(user_params)
     @user.requires_no_invitation_code!
     @user.requires_no_invitation_code!
-    @user.admin = admin
 
 
     respond_to do |format|
     respond_to do |format|
       if @user.save
       if @user.save
@@ -40,10 +38,8 @@ class Admin::UsersController < ApplicationController
   end
   end
 
 
   def update
   def update
-    admin = params[:user].delete(:admin)
     params[:user].except!(:password, :password_confirmation) if params[:user][:password].blank?
     params[:user].except!(:password, :password_confirmation) if params[:user][:password].blank?
-    @user.assign_attributes(params[:user])
-    @user.admin = admin
+    @user.assign_attributes(user_params)
 
 
     respond_to do |format|
     respond_to do |format|
       if @user.save
       if @user.save
@@ -106,6 +102,10 @@ class Admin::UsersController < ApplicationController
 
 
   private
   private
 
 
+  def user_params
+    params.require(:user).permit(:email, :username, :password, :password_confirmation, :admin)
+  end
+
   def find_user
   def find_user
     @user = User.find(params[:id])
     @user = User.find(params[:id])
   end
   end

+ 1 - 1
app/controllers/agents/dry_runs_controller.rb

@@ -14,7 +14,7 @@ module Agents
     end
     end
 
 
     def create
     def create
-      attrs = params[:agent] || {}
+      attrs = agent_params
       if agent = current_user.agents.find_by(id: params[:agent_id])
       if agent = current_user.agents.find_by(id: params[:agent_id])
         # POST /agents/:id/dry_run
         # POST /agents/:id/dry_run
         if attrs.present?
         if attrs.present?

+ 3 - 3
app/controllers/agents_controller.rb

@@ -160,7 +160,7 @@ class AgentsController < ApplicationController
     @agent = current_user.agents.find(params[:id])
     @agent = current_user.agents.find(params[:id])
 
 
     respond_to do |format|
     respond_to do |format|
-      if @agent.update_attributes(params[:agent])
+      if @agent.update_attributes(agent_params)
         format.html { redirect_back "'#{@agent.name}' was successfully updated.", return: agents_path }
         format.html { redirect_back "'#{@agent.name}' was successfully updated.", return: agents_path }
         format.json { render json: @agent, status: :ok, location: agent_path(@agent) }
         format.json { render json: @agent, status: :ok, location: agent_path(@agent) }
       else
       else
@@ -220,9 +220,9 @@ class AgentsController < ApplicationController
   end
   end
 
 
   def build_agent
   def build_agent
-    @agent = Agent.build_for_type(params[:agent].delete(:type),
+    @agent = Agent.build_for_type(agent_params[:type],
                                   current_user,
                                   current_user,
-                                  params[:agent])
+                                  agent_params.except(:type))
   end
   end
 
 
   def initialize_presenter
   def initialize_presenter

+ 11 - 0
app/controllers/application_controller.rb

@@ -60,4 +60,15 @@ class ApplicationController < ActionController::Base
       @basecamp_agent = current_user.agents.where(type: 'Agents::BasecampAgent').first
       @basecamp_agent = current_user.agents.where(type: 'Agents::BasecampAgent').first
     end
     end
   end
   end
+
+  def agent_params
+    return {} unless params[:agent]
+    @agent_params ||= begin
+      options = params[:agent].delete(:options) if params[:agent][:options].present?
+      params[:agent].permit(:memory, :name, :type, :schedule, :disabled, :keep_events_for, :propagate_immediately, :drop_pending_events,
+                            source_ids: [], receiver_ids: [], scenario_ids: [], controller_ids: [], control_target_ids: []).tap do |agent_params|
+        agent_params[:options] = options if options
+      end
+    end
+  end
 end
 end

+ 9 - 2
app/controllers/scenarios_controller.rb

@@ -69,7 +69,7 @@ class ScenariosController < ApplicationController
   end
   end
 
 
   def create
   def create
-    @scenario = current_user.scenarios.build(params[:scenario])
+    @scenario = current_user.scenarios.build(scenario_params)
 
 
     respond_to do |format|
     respond_to do |format|
       if @scenario.save
       if @scenario.save
@@ -86,7 +86,7 @@ class ScenariosController < ApplicationController
     @scenario = current_user.scenarios.find(params[:id])
     @scenario = current_user.scenarios.find(params[:id])
 
 
     respond_to do |format|
     respond_to do |format|
-      if @scenario.update_attributes(params[:scenario])
+      if @scenario.update_attributes(scenario_params)
         format.html { redirect_to @scenario, notice: 'This Scenario was successfully updated.' }
         format.html { redirect_to @scenario, notice: 'This Scenario was successfully updated.' }
         format.json { head :no_content }
         format.json { head :no_content }
       else
       else
@@ -115,4 +115,11 @@ class ScenariosController < ApplicationController
       format.json { head :no_content }
       format.json { head :no_content }
     end
     end
   end
   end
+
+  private
+
+  def scenario_params
+    params.require(:scenario).permit(:name, :description, :public, :source_url,
+                                     :tag_fg_color, :tag_bg_color, :icon, agent_ids: [])
+  end
 end
 end

+ 8 - 2
app/controllers/user_credentials_controller.rb

@@ -48,7 +48,7 @@ class UserCredentialsController < ApplicationController
   end
   end
 
 
   def create
   def create
-    @user_credential = current_user.user_credentials.build(params[:user_credential])
+    @user_credential = current_user.user_credentials.build(user_credential_params)
 
 
     respond_to do |format|
     respond_to do |format|
       if @user_credential.save
       if @user_credential.save
@@ -65,7 +65,7 @@ class UserCredentialsController < ApplicationController
     @user_credential = current_user.user_credentials.find(params[:id])
     @user_credential = current_user.user_credentials.find(params[:id])
 
 
     respond_to do |format|
     respond_to do |format|
-      if @user_credential.update_attributes(params[:user_credential])
+      if @user_credential.update_attributes(user_credential_params)
         format.html { redirect_to user_credentials_path, notice: 'Your credential was successfully updated.' }
         format.html { redirect_to user_credentials_path, notice: 'Your credential was successfully updated.' }
         format.json { head :no_content }
         format.json { head :no_content }
       else
       else
@@ -84,4 +84,10 @@ class UserCredentialsController < ApplicationController
       format.json { head :no_content }
       format.json { head :no_content }
     end
     end
   end
   end
+
+  private
+
+  def user_credential_params
+    params.require(:user_credential).permit(:credential_name, :credential_value, :mode)
+  end
 end
 end

+ 0 - 2
app/models/agent.rb

@@ -24,8 +24,6 @@ class Agent < ActiveRecord::Base
 
 
   EVENT_RETENTION_SCHEDULES = [["Forever", 0], ['1 hour', 1.hour], ['6 hours', 6.hours], ["1 day", 1.day], *([2, 3, 4, 5, 7, 14, 21, 30, 45, 90, 180, 365].map {|n| ["#{n} days", n.days] })]
   EVENT_RETENTION_SCHEDULES = [["Forever", 0], ['1 hour', 1.hour], ['6 hours', 6.hours], ["1 day", 1.day], *([2, 3, 4, 5, 7, 14, 21, 30, 45, 90, 180, 365].map {|n| ["#{n} days", n.days] })]
 
 
-  attr_accessible :options, :memory, :name, :type, :schedule, :controller_ids, :control_target_ids, :disabled, :source_ids, :receiver_ids, :scenario_ids, :keep_events_for, :propagate_immediately, :drop_pending_events
-
   json_serialize :options, :memory
   json_serialize :options, :memory
 
 
   validates_presence_of :name, :user
   validates_presence_of :name, :user

+ 0 - 2
app/models/agent_log.rb

@@ -2,8 +2,6 @@
 # in Agents' detail pages.  AgentLogs with a `level` of 4 or greater are considered "errors" and automatically update
 # in Agents' detail pages.  AgentLogs with a `level` of 4 or greater are considered "errors" and automatically update
 # Agents' `last_error_log_at` column.  These are often used to determine if an Agent is `working?`.
 # Agents' `last_error_log_at` column.  These are often used to determine if an Agent is `working?`.
 class AgentLog < ActiveRecord::Base
 class AgentLog < ActiveRecord::Base
-  attr_accessible :agent, :inbound_event, :level, :message, :outbound_event
-
   belongs_to :agent
   belongs_to :agent
   belongs_to :inbound_event, :class_name => "Event"
   belongs_to :inbound_event, :class_name => "Event"
   belongs_to :outbound_event, :class_name => "Event"
   belongs_to :outbound_event, :class_name => "Event"

+ 0 - 2
app/models/control_link.rb

@@ -1,7 +1,5 @@
 # A ControlLink connects Agents in a control flow from the `controller` to the `control_target`.
 # A ControlLink connects Agents in a control flow from the `controller` to the `control_target`.
 class ControlLink < ActiveRecord::Base
 class ControlLink < ActiveRecord::Base
-  attr_accessible :controller_id, :target_id
-
   belongs_to :controller, class_name: 'Agent', inverse_of: :control_links_as_controller
   belongs_to :controller, class_name: 'Agent', inverse_of: :control_links_as_controller
   belongs_to :control_target, class_name: 'Agent', inverse_of: :control_links_as_control_target
   belongs_to :control_target, class_name: 'Agent', inverse_of: :control_links_as_control_target
 end
 end

+ 0 - 2
app/models/event.rb

@@ -7,8 +7,6 @@ class Event < ActiveRecord::Base
   include JSONSerializedField
   include JSONSerializedField
   include LiquidDroppable
   include LiquidDroppable
 
 
-  attr_accessible :lat, :lng, :location, :payload, :user_id, :user, :expires_at
-
   acts_as_mappable
   acts_as_mappable
 
 
   json_serialize :payload
   json_serialize :payload

+ 0 - 2
app/models/link.rb

@@ -1,7 +1,5 @@
 # A Link connects Agents in a directed Event flow from the `source` to the `receiver`.
 # A Link connects Agents in a directed Event flow from the `source` to the `receiver`.
 class Link < ActiveRecord::Base
 class Link < ActiveRecord::Base
-  attr_accessible :source_id, :receiver_id
-
   belongs_to :source, :class_name => "Agent", :inverse_of => :links_as_source
   belongs_to :source, :class_name => "Agent", :inverse_of => :links_as_source
   belongs_to :receiver, :class_name => "Agent", :inverse_of => :links_as_receiver
   belongs_to :receiver, :class_name => "Agent", :inverse_of => :links_as_receiver
 
 

+ 0 - 3
app/models/scenario.rb

@@ -1,9 +1,6 @@
 class Scenario < ActiveRecord::Base
 class Scenario < ActiveRecord::Base
   include HasGuid
   include HasGuid
 
 
-  attr_accessible :name, :agent_ids, :description, :public, :source_url,
-                  :tag_fg_color, :tag_bg_color, :icon
-
   belongs_to :user, :counter_cache => :scenario_count, :inverse_of => :scenarios
   belongs_to :user, :counter_cache => :scenario_count, :inverse_of => :scenarios
   has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :scenario
   has_many :scenario_memberships, :dependent => :destroy, :inverse_of => :scenario
   has_many :agents, :through => :scenario_memberships, :inverse_of => :scenarios
   has_many :agents, :through => :scenario_memberships, :inverse_of => :scenarios

+ 0 - 2
app/models/service.rb

@@ -1,6 +1,4 @@
 class Service < ActiveRecord::Base
 class Service < ActiveRecord::Base
-  attr_accessible :provider, :name, :token, :secret, :refresh_token, :expires_at, :global, :options, :uid
-
   serialize :options, Hash
   serialize :options, Hash
 
 
   belongs_to :user, :inverse_of => :services
   belongs_to :user, :inverse_of => :services

+ 0 - 5
app/models/user.rb

@@ -12,11 +12,6 @@ class User < ActiveRecord::Base
   # This is in addition to a real persisted field like 'username'
   # This is in addition to a real persisted field like 'username'
   attr_accessor :login
   attr_accessor :login
 
 
-  ACCESSIBLE_ATTRIBUTES = [ :email, :username, :login, :password, :password_confirmation, :remember_me, :invitation_code ]
-
-  attr_accessible *ACCESSIBLE_ATTRIBUTES
-  attr_accessible *(ACCESSIBLE_ATTRIBUTES + [:admin]), :as => :admin
-
   validates_presence_of :username
   validates_presence_of :username
   validates :username, uniqueness: { case_sensitive: false }
   validates :username, uniqueness: { case_sensitive: false }
   validates_format_of :username, :with => /\A[a-zA-Z0-9_-]{3,15}\Z/, :message => "can only contain letters, numbers, underscores, and dashes, and must be between 3 and 15 characters in length."
   validates_format_of :username, :with => /\A[a-zA-Z0-9_-]{3,15}\Z/, :message => "can only contain letters, numbers, underscores, and dashes, and must be between 3 and 15 characters in length."

+ 0 - 2
app/models/user_credential.rb

@@ -1,8 +1,6 @@
 class UserCredential < ActiveRecord::Base
 class UserCredential < ActiveRecord::Base
   MODES = %w[text java_script]
   MODES = %w[text java_script]
 
 
-  attr_accessible :credential_name, :credential_value, :mode
-
   belongs_to :user
   belongs_to :user
 
 
   validates_presence_of :credential_name
   validates_presence_of :credential_name

+ 0 - 6
config/application.rb

@@ -39,12 +39,6 @@ module Huginn
     # like if you have constraints or database-specific column types
     # like if you have constraints or database-specific column types
     # config.active_record.schema_format = :sql
     # config.active_record.schema_format = :sql
 
 
-    # Enforce whitelist mode for mass assignment.
-    # This will create an empty whitelist of attributes available for mass-assignment for all models
-    # in your app. As such, your models will need to explicitly whitelist or blacklist accessible
-    # parameters by using an attr_accessible or attr_protected declaration.
-    config.active_record.whitelist_attributes = true
-
     # Do not swallow errors in after_commit/after_rollback callbacks.
     # Do not swallow errors in after_commit/after_rollback callbacks.
     config.active_record.raise_in_transactional_callbacks = true
     config.active_record.raise_in_transactional_callbacks = true
 
 

+ 2 - 2
config/environments/development.rb

@@ -26,8 +26,8 @@ Huginn::Application.configure do
   # Only use best-standards-support built into browsers
   # Only use best-standards-support built into browsers
   config.action_dispatch.best_standards_support = :builtin
   config.action_dispatch.best_standards_support = :builtin
 
 
-  # Raise exception on mass assignment protection for Active Record models
-  config.active_record.mass_assignment_sanitizer = :strict
+  # Raise exception for unpermitted parameters
+  config.action_controller.action_on_unpermitted_parameters = :raise
 
 
   # Raise an error on page load if there are pending migrations.
   # Raise an error on page load if there are pending migrations.
   config.active_record.migration_error = :page_load
   config.active_record.migration_error = :page_load

+ 2 - 2
config/environments/test.rb

@@ -33,8 +33,8 @@ Huginn::Application.configure do
 
 
   config.action_mailer.raise_delivery_errors = true
   config.action_mailer.raise_delivery_errors = true
 
 
-  # Raise exception on mass assignment protection for Active Record models
-  config.active_record.mass_assignment_sanitizer = :strict
+  # Raise exception for unpermitted parameters
+  config.action_controller.action_on_unpermitted_parameters = :raise
 
 
   # Randomize the order test cases are executed.
   # Randomize the order test cases are executed.
   config.active_support.test_order = :random
   config.active_support.test_order = :random

+ 1 - 1
spec/controllers/admin/users_controller_spec.rb

@@ -15,7 +15,7 @@ describe Admin::UsersController do
       it 'does not import the default scenario' do
       it 'does not import the default scenario' do
         stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
         stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
         sign_in users(:jane)
         sign_in users(:jane)
-        post :create, :user => {}
+        post :create, :user => {username: 'user'}
       end
       end
     end
     end
   end
   end

+ 7 - 0
spec/controllers/agents_controller_spec.rb

@@ -280,6 +280,13 @@ describe AgentsController do
       expect(response).to render_template("edit")
       expect(response).to render_template("edit")
     end
     end
 
 
+    it 'does not allow to modify the agents user_id' do
+      sign_in users(:bob)
+      expect {
+        post :update, :id => agents(:bob_website_agent).to_param, :agent => valid_attributes(:user_id => users(:jane).id)
+      }.to raise_error(ActionController::UnpermittedParameters)
+    end
+
     describe "redirecting back" do
     describe "redirecting back" do
       before do
       before do
         sign_in users(:bob)
         sign_in users(:bob)

+ 7 - 1
spec/controllers/scenarios_controller_spec.rb

@@ -116,7 +116,7 @@ describe ScenariosController do
     it "will not create Scenarios for other users" do
     it "will not create Scenarios for other users" do
       expect {
       expect {
         post :create, :scenario => valid_attributes(:user_id => users(:jane).id)
         post :create, :scenario => valid_attributes(:user_id => users(:jane).id)
-      }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
+      }.to raise_error(ActionController::UnpermittedParameters)
     end
     end
   end
   end
 
 
@@ -138,6 +138,12 @@ describe ScenariosController do
       expect(assigns(:scenario)).to have(1).errors_on(:name)
       expect(assigns(:scenario)).to have(1).errors_on(:name)
       expect(response).to render_template("edit")
       expect(response).to render_template("edit")
     end
     end
+
+    it 'adds an agent to the scenario' do
+      expect {
+        post :update, :id => scenarios(:bob_weather).to_param, :scenario => { :name => "new_name", :public => "1", agent_ids: scenarios(:bob_weather).agent_ids + [agents(:bob_website_agent).id] }
+      }.to change { scenarios(:bob_weather).agent_ids.length }.by(1)
+    end
   end
   end
 
 
   describe 'PUT enable_or_disable_all_agents' do
   describe 'PUT enable_or_disable_all_agents' do

+ 1 - 1
spec/controllers/user_credentials_controller_spec.rb

@@ -69,7 +69,7 @@ describe UserCredentialsController do
     it "will not create UserCredentials for other users" do
     it "will not create UserCredentials for other users" do
       expect {
       expect {
         post :create, :user_credential => valid_attributes(:user_id => users(:jane).id)
         post :create, :user_credential => valid_attributes(:user_id => users(:jane).id)
-      }.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
+      }.to raise_error(ActionController::UnpermittedParameters)
     end
     end
   end
   end
 
 

+ 9 - 3
spec/controllers/users/registrations_controller_spec.rb

@@ -5,13 +5,16 @@ module Users
     include Devise::TestHelpers
     include Devise::TestHelpers
 
 
     describe "POST create" do
     describe "POST create" do
+      before do
+        @request.env["devise.mapping"] = Devise.mappings[:user]
+      end
+
       context 'with valid params' do
       context 'with valid params' do
         it "imports the default scenario for the new user" do
         it "imports the default scenario for the new user" do
           mock(DefaultScenarioImporter).import(is_a(User))
           mock(DefaultScenarioImporter).import(is_a(User))
 
 
-          @request.env["devise.mapping"] = Devise.mappings[:user]
           post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
           post :create, :user => {username: 'jdoe', email: 'jdoe@example.com',
-            password: 's3cr3t55', password_confirmation: 's3cr3t55', admin: false, invitation_code: 'try-huginn'}
+            password: 's3cr3t55', password_confirmation: 's3cr3t55', invitation_code: 'try-huginn'}
         end
         end
       end
       end
 
 
@@ -19,10 +22,13 @@ module Users
         it "does not import the default scenario" do
         it "does not import the default scenario" do
           stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
           stub(DefaultScenarioImporter).import(is_a(User)) { fail "Should not attempt import" }
 
 
-          @request.env["devise.mapping"] = Devise.mappings[:user]
           setup_controller_for_warden
           setup_controller_for_warden
           post :create, :user => {}
           post :create, :user => {}
         end
         end
+
+        it 'does not allow to set the admin flag' do
+          expect { post :create, :user => {admin: 'true'} }.to raise_error(ActionController::UnpermittedParameters)
+        end
       end
       end
     end
     end
   end
   end

+ 0 - 8
spec/models/user_credential_spec.rb

@@ -8,14 +8,6 @@ describe UserCredential do
     it { should validate_presence_of(:user_id) }
     it { should validate_presence_of(:user_id) }
   end
   end
 
 
-  describe "mass assignment" do
-    it { should allow_mass_assignment_of :credential_name }
-
-    it { should allow_mass_assignment_of :credential_value }
-
-    it { should_not allow_mass_assignment_of :user_id }
-  end
-
   describe "cleaning fields" do
   describe "cleaning fields" do
     it "should trim whitespace" do
     it "should trim whitespace" do
       user_credential = user_credentials(:bob_aws_key)
       user_credential = user_credentials(:bob_aws_key)