Skip to content

Commit

Permalink
Add MassAssignmentRestriction#column
Browse files Browse the repository at this point in the history
Update MassAssignmentRestriction.create to take the column. Also, have it always include
the class in the error message, even if the class is anonymous.

For consistency, the column is always saved as string, since the mass assignment methods
are generally used with string-keyed hashes provided by the user, and the comparison to
allowed methods is done using strings.

Simplify some of the related test code.
  • Loading branch information
jeremyevans committed Sep 18, 2023
1 parent 1e00c84 commit f24702d
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 22 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== master

* Add MassAssignmentRestriction#model and #column for getting the model instance and related column for mass assignment errors (artofhuman, jeremyevans) (#2079)

* Stop using base64 library in column_encryption plugin (jeremyevans)

=== 5.72.0 (2023-09-01)
Expand Down
9 changes: 5 additions & 4 deletions lib/sequel/model/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2031,19 +2031,20 @@ def set_restricted(hash, type)
meths = setter_methods(type)
strict = strict_param_setting
hash.each do |k,v|
k = k.to_s
m = "#{k}="
if meths.include?(m)
set_column_value(m, v)
elsif strict
# Avoid using respond_to? or creating symbols from user input
if public_methods.map(&:to_s).include?(m)
if Array(model.primary_key).map(&:to_s).member?(k.to_s) && model.restrict_primary_key?
raise MassAssignmentRestriction.create("#{k} is a restricted primary key", self)
if Array(model.primary_key).map(&:to_s).member?(k) && model.restrict_primary_key?
raise MassAssignmentRestriction.create("#{k} is a restricted primary key", self, k)
else
raise MassAssignmentRestriction.create("#{k} is a restricted column", self)
raise MassAssignmentRestriction.create("#{k} is a restricted column", self, k)
end
else
raise MassAssignmentRestriction.create("method #{m} doesn't exist", self)
raise MassAssignmentRestriction.create("method #{m} doesn't exist", self, k)
end
end
end
Expand Down
14 changes: 9 additions & 5 deletions lib/sequel/model/exceptions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,15 @@ class MassAssignmentRestriction < Error
# The Sequel::Model object related to this exception.
attr_reader :model

def self.create(msg, model)
msg += " for class #{model.class.name}" if model.class.name
new(msg).tap do |err|
err.instance_variable_set(:@model, model)
end
# The column related to this exception, as a string.
attr_reader :column

# Create an instance of this class with the model and column set.
def self.create(msg, model, column)
e = new("#{msg} for class #{model.class.inspect}")
e.instance_variable_set(:@model, model)
e.instance_variable_set(:@column, column)
e
end
end

Expand Down
4 changes: 3 additions & 1 deletion spec/extensions/csv_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ def self.name; 'Album' end
it "should raise an error if attempting to set a restricted column and :all_columns is not used" do
@Artist.restrict_primary_key
err = proc{@Artist.from_csv(@artist.to_csv)}.must_raise(Sequel::MassAssignmentRestriction)
err.message.must_equal("id is a restricted primary key for class Artist")
err.message.must_include("id is a restricted primary key for class ")
err.model.class.must_equal @Artist
err.column.must_equal "id"
end

it "should use a dataset's selected columns" do
Expand Down
14 changes: 6 additions & 8 deletions spec/model/base_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -665,25 +665,23 @@ class ModelTest2 < Sequel::Model(@db[:foo])

it "should raise an error if a missing/restricted column/method is accessed" do
err = proc{@c.new(:a=>1)}.must_raise(Sequel::MassAssignmentRestriction)
err.message.must_equal("method a= doesn't exist")
err.message.must_include("method a= doesn't exist for class ")
err.column.must_equal("a")
proc{@c.create(:a=>1)}.must_raise(Sequel::MassAssignmentRestriction)
c = @c.new
proc{c.set(:a=>1)}.must_raise(Sequel::MassAssignmentRestriction)
proc{c.update(:a=>1)}.must_raise(Sequel::MassAssignmentRestriction)
end

it "should add class name to error message" do
item = Class.new(Sequel::Model(:items)) do
columns :id
end

item.class_eval do
def self.name; 'Item' end
end
item = Class.new(Sequel::Model(:items))
item.columns :id
def item.inspect; 'Item' end

err = proc{item.new(:a=>1)}.must_raise(Sequel::MassAssignmentRestriction)
err.message.must_equal("method a= doesn't exist for class Item")
err.model.class.must_equal(item)
err.column.must_equal("a")
end

it "should be disabled by strict_param_setting = false" do
Expand Down
13 changes: 9 additions & 4 deletions spec/model/exceptions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@
describe Sequel::MassAssignmentRestriction, "#create" do
it "should set model attr" do
model_cls = Class.new
model_cls.class_eval do
def self.name; 'TestModel' end
end

model = model_cls.new
err = Sequel::MassAssignmentRestriction.create("method foo doesn't exist", model)
err = Sequel::MassAssignmentRestriction.create("method foo doesn't exist", model, "foo")
err.message.must_include("method foo doesn't exist for class ")
err.model.must_equal(model)
err.column.must_equal("foo")

def model_cls.inspect; 'TestModel' end
model = model_cls.new
err = Sequel::MassAssignmentRestriction.create("method foo doesn't exist", model, "foo")
err.message.must_equal("method foo doesn't exist for class TestModel")
err.model.must_equal(model)
err.column.must_equal("foo")
end
end

0 comments on commit f24702d

Please sign in to comment.