protector 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 823427ab7061d2b26ab6835d3c4bbfd17abe6430
4
- data.tar.gz: dfa3edc7c59fca658f81ef6f1d377890a6ac9c1a
3
+ metadata.gz: 4f2e7f8f389acbad91383ebfd69716fb4f47f9ff
4
+ data.tar.gz: c0afd232c22534c7e98b5147c460d84497d2079d
5
5
  SHA512:
6
- metadata.gz: 4897c4ccea8d4b074158ba15b33395ec04522c067dc3a4c065366a6c8ceb81a1b9133c3862ed1625818c2f4d260107483f078277403b5375f09456d02f8e24d1
7
- data.tar.gz: c9229df3995d7d6f71e0d71d638e6d1273ca165977badcdec63f643074a4598dbd370ae446c58cfa9f4df6bd418f7959c577cf19d1b1c7cd611764d062043557
6
+ metadata.gz: 513dc71a0f5555eacd2b6a70d03df9952803a6a2284cd7e3798d372550c3074169fb1bf8a4421944e97f791afa9454022c6bc0d7331e5fe56066959704dd8f9d
7
+ data.tar.gz: d7116a42ed32d2af53a90ed1e8a5db2c42a4431d9379875af5a58f4aa63aaaa90888f124553668f3223779d0fbe6b8d504c5b5081f4c05783c5e2daf5cad1f0b
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
+ gemfiles/
7
8
  InstalledFiles
8
9
  _yardoc
9
10
  coverage
@@ -15,4 +16,4 @@ spec/reports
15
16
  test/tmp
16
17
  test/version_tmp
17
18
  tmp
18
- spec/internal/log
19
+ spec/internal/log
data/.travis.yml CHANGED
@@ -1,6 +1,4 @@
1
- before_install:
2
- - gem install bundler
3
1
  rvm:
4
2
  - 1.9.3
5
3
  - jruby-19mode
6
- - 2.0.0
4
+ - 2.0.0
data/Gemfile CHANGED
@@ -5,12 +5,12 @@ gem 'rake'
5
5
  gem 'colored'
6
6
  gem 'pry'
7
7
  gem 'rspec'
8
+ gem 'simplecov', require: false
9
+ gem 'simplecov-summary'
8
10
 
9
11
  gem 'appraisal'
10
12
 
11
13
  gem 'sqlite3', platform: :ruby
12
14
  gem 'jdbc-sqlite3', platform: :jruby, require: 'jdbc/sqlite3'
13
15
 
14
- gem 'coveralls', require: false
15
-
16
16
  gem 'ruby-prof', platform: :ruby
data/README.md CHANGED
@@ -21,6 +21,7 @@ We are working hard to extend the list with:
21
21
  Protector is an extension and therefore hides deeply inside your ORM library making itself compatible to the most gems you use. Sometimes however, you might need additional integration to take the best from it:
22
22
 
23
23
  * [Protector and Strong Parameters](https://github.com/inossidabile/protector/wiki/Protector-and-Strong-Parameters)
24
+ * [Protector and CanCan](https://github.com/inossidabile/protector/wiki/Protector-and-CanCan)
24
25
  * [Protector and SimpleForm](https://github.com/inossidabile/protector/wiki/Protector-and-SimpleForm)
25
26
 
26
27
  ## Basics
@@ -9,15 +9,20 @@ module Protector
9
9
  include Protector::DSL::Base
10
10
  include Protector::DSL::Entry
11
11
 
12
+ # We need this to make sure no ActiveRecord classes managed
13
+ # to cache db scheme and create corresponding methods since
14
+ # we want to modify the way they get created
12
15
  ObjectSpace.each_object(Class).each do |klass|
13
16
  klass.undefine_attribute_methods if klass < self
14
17
  end
15
18
 
16
19
  validate do
17
20
  return unless protector_subject?
18
- if (new_record? && !creatable?) || (!new_record? && !updatable?)
19
- errors[:base] << I18n.t('protector.invalid')
20
- end
21
+
22
+ method = new_record? ? :first_uncreatable_field : :first_unupdatable_field
23
+ field = protector_meta.send(method, protector_changed)
24
+
25
+ errors[:base] << I18n.t('protector.invalid', field: field) if field
21
26
  end
22
27
 
23
28
  before_destroy do
@@ -86,6 +91,11 @@ module Protector
86
91
  end
87
92
  end
88
93
 
94
+ # Gathers real changed values bypassing restrictions
95
+ def protector_changed
96
+ HashWithIndifferentAccess[changed.map{|field| [field, read_attribute(field)]}]
97
+ end
98
+
89
99
  # Storage for {Protector::DSL::Meta::Box}
90
100
  def protector_meta(subject=protector_subject)
91
101
  @protector_meta ||= self.class.protector_meta.evaluate(subject, self)
@@ -102,14 +112,12 @@ module Protector
102
112
 
103
113
  # Checks if current model can be created in the context of current subject
104
114
  def creatable?
105
- fields = HashWithIndifferentAccess[changed.map{|field| [field, read_attribute(field)]}]
106
- protector_meta.creatable?(fields)
115
+ protector_meta.creatable? protector_changed
107
116
  end
108
117
 
109
118
  # Checks if current model can be updated in the context of current subject
110
119
  def updatable?
111
- fields = HashWithIndifferentAccess[changed.map{|field| [field, read_attribute(field)]}]
112
- protector_meta.updatable?(fields)
120
+ protector_meta.updatable? protector_changed
113
121
  end
114
122
 
115
123
  # Checks if current model can be destroyed in the context of current subject
@@ -111,7 +111,7 @@ module Protector
111
111
  @records
112
112
  end
113
113
 
114
- # Swaps `includes` with `preload` whether it's not referenced or merges
114
+ # Swaps `includes` with `preload` if it's not referenced or merges
115
115
  # security scope of proper class otherwise
116
116
  def protector_substitute_includes(subject, relation)
117
117
  if eager_loading?
@@ -124,15 +124,10 @@ module Protector
124
124
  if meta.scoped?
125
125
  unscoped = klass.unscoped
126
126
 
127
- # AR 4 has awfull inconsistency when it comes to method `all`
128
- # We have to mimic base class behaviour for relation we get from `unscoped`
129
- if Protector::Adapters::ActiveRecord.modern?
130
- class <<unscoped
131
- def all
132
- self
133
- end
134
- end
135
- end
127
+ # `unscoped` gets us a relation but Protector scope is supposed
128
+ # to work with AR::Base. Some versions of AR have those uncompatible
129
+ # so we have to workaround it :(
130
+ unscoped.protector_mimic_base!
136
131
 
137
132
  # Finally we merge unscoped basic relation extended with protection scope
138
133
  relation = relation.merge unscoped.instance_eval(&meta.scope_proc)
@@ -146,6 +141,20 @@ module Protector
146
141
  relation
147
142
  end
148
143
 
144
+ # Makes instance of Relation duck-type compatible to AR::Base to allow proper
145
+ # protection block execution with itself
146
+ def protector_mimic_base!
147
+ return unless Protector::Adapters::ActiveRecord.modern?
148
+
149
+ class <<self
150
+ # AR 4 has awfull inconsistency when it comes to method `all`
151
+ # We have to mimic base class behaviour for relation we get from `unscoped`
152
+ def all
153
+ self
154
+ end
155
+ end
156
+ end
157
+
149
158
  # Indexes `includes` format by actual entity class
150
159
  #
151
160
  # Turns `{foo: :bar}` into `[[Foo, :foo], [Bar, {foo: :bar}]`
@@ -31,6 +31,11 @@ module Protector
31
31
  end
32
32
  end
33
33
 
34
+ # Gathers real values of given fields bypassing restrictions
35
+ def protector_changed(fields)
36
+ HashWithIndifferentAccess[fields.map{|x| [x.to_s, @values[x]]}]
37
+ end
38
+
34
39
  # Storage for {Protector::DSL::Meta::Box}
35
40
  def protector_meta(subject=protector_subject)
36
41
  @protector_meta ||= self.class.protector_meta.evaluate(subject, self)
@@ -45,13 +50,12 @@ module Protector
45
50
  # Checks if current model can be created in the context of current subject
46
51
  def creatable?
47
52
  fields = HashWithIndifferentAccess[keys.map{|x| [x.to_s, @values[x]]}]
48
- protector_meta.creatable?(fields)
53
+ protector_meta.creatable? protector_changed(keys)
49
54
  end
50
55
 
51
56
  # Checks if current model can be updated in the context of current subject
52
57
  def updatable?
53
- fields = HashWithIndifferentAccess[changed_columns.map{|x| [x.to_s, @values[x]]}]
54
- protector_meta.updatable?(fields)
58
+ protector_meta.updatable? protector_changed(changed_columns)
55
59
  end
56
60
 
57
61
  # Checks if current model can be destroyed in the context of current subject
@@ -65,10 +69,15 @@ module Protector
65
69
 
66
70
  # Basic security validations
67
71
  def validate
68
- super
69
- return unless protector_subject?
70
- method = new? ? :creatable? : :updatable?
71
- errors.add(:base, I18n.t('protector.invalid')) unless __send__(method)
72
+ super; return unless protector_subject?
73
+
74
+ field = if new?
75
+ protector_meta.first_uncreatable_field protector_changed(keys)
76
+ else
77
+ protector_meta.first_unupdatable_field protector_changed(changed_columns)
78
+ end
79
+
80
+ errors.add :base, I18n.t('protector.invalid', field: field) if field
72
81
  end
73
82
 
74
83
  # Destroy availability check
data/lib/protector/dsl.rb CHANGED
@@ -159,11 +159,19 @@ module Protector
159
159
  modifiable? :create, fields
160
160
  end
161
161
 
162
+ def first_uncreatable_field(fields)
163
+ first_unmodifiable_field :create, fields
164
+ end
165
+
162
166
  # Checks whether you can update a model with given field in context of current subject
163
167
  def updatable?(fields=false)
164
168
  modifiable? :update, fields
165
169
  end
166
170
 
171
+ def first_unupdatable_field(fields)
172
+ first_unmodifiable_field :update, fields
173
+ end
174
+
167
175
  # Checks whether you can destroy a model in context of current subject
168
176
  def destroyable?
169
177
  @destroyable
@@ -181,23 +189,27 @@ module Protector
181
189
 
182
190
  private
183
191
 
184
- def modifiable?(part, fields)
185
- keys = @access[part].keys
186
- return false unless keys.length > 0
187
-
188
- if fields
189
- return false if (fields.keys - keys).length > 0
190
-
191
- fields.each do |k,v|
192
- case x = @access[part][k]
193
- when Range
194
- return false unless x.include?(v)
195
- when Proc
196
- return false unless x.call(v)
197
- end
192
+ def first_unmodifiable_field(part, fields)
193
+ diff = fields.keys - @access[part].keys
194
+ return diff.first if diff.length > 0
195
+
196
+ fields.each do |k,v|
197
+ case x = @access[part][k]
198
+ when Range
199
+ return k unless x.include?(v)
200
+ when Proc
201
+ return k unless x.call(v)
202
+ else
203
+ return k if x != nil && x != v
198
204
  end
199
205
  end
200
206
 
207
+ false
208
+ end
209
+
210
+ def modifiable?(part, fields)
211
+ return false unless @access[part].keys.length > 0
212
+ return false if fields && first_unmodifiable_field(part, fields)
201
213
  true
202
214
  end
203
215
  end
@@ -1,4 +1,4 @@
1
1
  module Protector
2
2
  # Gem version
3
- VERSION = "0.5.3"
3
+ VERSION = "0.5.4"
4
4
  end
data/locales/en.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  en:
2
2
  protector:
3
- invalid: "Access denied"
3
+ invalid: "Access denied to '%{field}'"
@@ -12,8 +12,13 @@ if defined?(ActiveRecord)
12
12
 
13
13
  included do |klass|
14
14
  protect do |x|
15
- scope{ where('1=0') } if x == '-'
16
- scope{ where(klass.table_name => {number: 999}) } if x == '+'
15
+ if x == '-'
16
+ scope{ where('1=0') }
17
+ elsif x == '+'
18
+ scope{ where(klass.table_name => {number: 999}) }
19
+ elsif !Protector.config.paranoid && Protector::Adapters::ActiveRecord.modern?
20
+ scope { all }
21
+ end
17
22
 
18
23
  can :view, :dummy_id unless x == '-'
19
24
  end
@@ -54,6 +59,7 @@ if defined?(ActiveRecord)
54
59
  describe Protector::Adapters::ActiveRecord::Base do
55
60
  let(:dummy) do
56
61
  Class.new(ActiveRecord::Base) do
62
+ def self.model_name; ActiveModel::Name.new(self, nil, "dummy"); end
57
63
  self.table_name = "dummies"
58
64
  scope :none, where('1 = 0') unless respond_to?(:none)
59
65
  end
data/spec/lib/dsl_spec.rb CHANGED
@@ -80,7 +80,8 @@ describe Protector::DSL do
80
80
  user.should == 'user' if user
81
81
  entry.should == 'entry' if user
82
82
 
83
- can :update, %w(field1 field2 field3),
83
+ can :update, %w(field1 field2),
84
+ field3: 1,
84
85
  field4: 0..5,
85
86
  field5: l
86
87
 
@@ -117,7 +118,7 @@ describe Protector::DSL do
117
118
  update: {
118
119
  "field1" => nil,
119
120
  "field2" => nil,
120
- "field3" => nil,
121
+ "field3" => 1,
121
122
  "field4" => 0..5,
122
123
  "field5" => l
123
124
  },
@@ -140,10 +141,20 @@ describe Protector::DSL do
140
141
  data.updatable?.should == true
141
142
  end
142
143
 
144
+ it "gets first unupdatable field" do
145
+ data = @meta.evaluate('user', 'entry')
146
+ data.first_unupdatable_field('field1' => 1, 'field6' => 2, 'field7' => 3).should == 'field6'
147
+ end
148
+
143
149
  it "marks creatable" do
144
150
  data = @meta.evaluate('user', 'entry')
145
151
  data.creatable?.should == false
146
152
  end
153
+
154
+ it "gets first uncreatable field" do
155
+ data = @meta.evaluate('user', 'entry')
156
+ data.first_uncreatable_field('field1' => 1, 'field6' => 2).should == 'field1'
157
+ end
147
158
  end
148
159
 
149
160
  context "custom methods" do
@@ -1,7 +1,7 @@
1
1
  RSpec::Matchers.define :invalidate do
2
2
  match do |actual|
3
3
  actual.save.should == false
4
- actual.errors[:base].should == ["Access denied"]
4
+ actual.errors[:base][0].starts_with?("Access denied to").should == true
5
5
  end
6
6
  end
7
7
 
@@ -2,7 +2,7 @@ RSpec::Matchers.define :invalidate do
2
2
  match do |actual|
3
3
  DB.transaction do
4
4
  expect{ actual.save }.to raise_error
5
- actual.errors.on(:base).should == ["Access denied"]
5
+ actual.errors.on(:base)[0].starts_with?("Access denied to").should == true
6
6
  raise Sequel::Rollback
7
7
  end
8
8
 
@@ -1,9 +1,21 @@
1
- Bundler.require
1
+ require 'simplecov'
2
+ require 'simplecov-summary'
3
+
4
+ SimpleCov.start do
5
+ command_name File.basename(ENV['BUNDLE_GEMFILE'], '.gemfile')
6
+
7
+ add_filter '/spec/'
8
+
9
+ add_group 'DSL', 'lib/protector/dsl.rb'
10
+ add_group 'Railtie', 'lib/protector/engine.rb'
11
+ add_group 'ActiveRecord', 'lib/protector/adapters/active_record'
12
+ add_group 'Sequel', 'lib/protector/adapters/sequel'
2
13
 
3
- require 'coveralls'
4
- Coveralls.wear! if Coveralls.should_run?
14
+ at_exit do; end
15
+ end
16
+
17
+ Bundler.require
5
18
 
6
- require 'protector'
7
19
  require_relative 'contexts/paranoid'
8
20
  require_relative 'examples/model'
9
21
 
@@ -12,6 +24,16 @@ RSpec.configure do |config|
12
24
  config.run_all_when_everything_filtered = true
13
25
  config.filter_run :focus
14
26
 
27
+ config.after(:suite) do
28
+ if SimpleCov.running
29
+ silence_stream(STDOUT) do
30
+ SimpleCov::Formatter::HTMLFormatter.new.format(SimpleCov.result)
31
+ end
32
+
33
+ SimpleCov::Formatter::SummaryFormatter.new.format(SimpleCov.result)
34
+ end
35
+ end
36
+
15
37
  # Run specs in random order to surface order dependencies. If you find an
16
38
  # order dependency and want to debug it, you can fix the order by providing
17
39
  # the seed, which is printed after each run.
@@ -101,13 +101,14 @@ shared_examples_for "a model" do
101
101
  end
102
102
 
103
103
  it "marks blocked" do
104
- d = dummy.new(string: 'bam', number: 1)
105
- d.restrict!('!').creatable?.should == false
104
+ d = dummy.new(string: 'bam', number: 1).restrict!('!')
105
+ d.creatable?.should == false
106
106
  end
107
107
 
108
108
  it "marks allowed" do
109
- d = dummy.new(string: 'bam')
110
- d.restrict!('!').creatable?.should == true
109
+ d = dummy.new(string: 'bam').restrict!('!')
110
+ $debug = true
111
+ d.creatable?.should == true
111
112
  end
112
113
 
113
114
  it "invalidates" do
@@ -180,6 +181,36 @@ shared_examples_for "a model" do
180
181
  d.should validate
181
182
  end
182
183
  end
184
+
185
+ context "by direct values" do
186
+ before(:each) do
187
+ dummy.instance_eval do
188
+ protect do
189
+ can :create, number: 5
190
+ end
191
+ end
192
+ end
193
+
194
+ it "marks blocked" do
195
+ d = dummy.new(number: 500)
196
+ d.restrict!('!').creatable?.should == false
197
+ end
198
+
199
+ it "marks allowed" do
200
+ d = dummy.new(number: 5)
201
+ d.restrict!('!').creatable?.should == true
202
+ end
203
+
204
+ it "invalidates" do
205
+ d = dummy.new(number: 500).restrict!('!')
206
+ d.should invalidate
207
+ end
208
+
209
+ it "validates" do
210
+ d = dummy.new(number: 5).restrict!('!')
211
+ d.should validate
212
+ end
213
+ end
183
214
  end
184
215
 
185
216
  #
@@ -307,6 +338,40 @@ shared_examples_for "a model" do
307
338
  d.should validate
308
339
  end
309
340
  end
341
+
342
+ context "by direct values" do
343
+ before(:each) do
344
+ dummy.instance_eval do
345
+ protect do
346
+ can :update, number: 5
347
+ end
348
+ end
349
+ end
350
+
351
+ it "marks blocked" do
352
+ d = dummy.first
353
+ assign!(d, number: 500)
354
+ d.restrict!('!').updatable?.should == false
355
+ end
356
+
357
+ it "marks allowed" do
358
+ d = dummy.first
359
+ assign!(d, number: 5)
360
+ d.restrict!('!').updatable?.should == true
361
+ end
362
+
363
+ it "invalidates" do
364
+ d = dummy.first.restrict!('!')
365
+ assign!(d, number: 500)
366
+ d.should invalidate
367
+ end
368
+
369
+ it "validates" do
370
+ d = dummy.first.restrict!('!')
371
+ assign!(d, number: 5)
372
+ d.should validate
373
+ end
374
+ end
310
375
  end
311
376
 
312
377
  #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protector
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boris Staal
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-14 00:00:00.000000000 Z
11
+ date: 2013-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -55,18 +55,6 @@ files:
55
55
  - LICENSE.txt
56
56
  - README.md
57
57
  - Rakefile
58
- - gemfiles/AR_3.2.gemfile
59
- - gemfiles/AR_3.2.gemfile.lock
60
- - gemfiles/AR_4.gemfile
61
- - gemfiles/AR_4.gemfile.lock
62
- - gemfiles/Mongoid.gemfile
63
- - gemfiles/Mongoid.gemfile.lock
64
- - gemfiles/Rails_3.2.gemfile
65
- - gemfiles/Rails_3.2.gemfile.lock
66
- - gemfiles/Rails_4.gemfile
67
- - gemfiles/Rails_4.gemfile.lock
68
- - gemfiles/Sequel.gemfile
69
- - gemfiles/Sequel.gemfile.lock
70
58
  - lib/protector.rb
71
59
  - lib/protector/adapters/active_record.rb
72
60
  - lib/protector/adapters/active_record/association.rb
@@ -119,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
107
  version: '0'
120
108
  requirements: []
121
109
  rubyforge_project:
122
- rubygems_version: 2.0.2
110
+ rubygems_version: 2.0.6
123
111
  signing_key:
124
112
  specification_version: 4
125
113
  summary: 'Protector is a successor to the Heimdallr gem: it hits the same goals keeping
@@ -1,19 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "rake"
6
- gem "colored"
7
- gem "pry"
8
- gem "rspec"
9
- gem "guard"
10
- gem "guard-rspec"
11
- gem "appraisal"
12
- gem "sqlite3", :platform=>:ruby
13
- gem "jdbc-sqlite3", :platform=>:jruby, :require=>"jdbc/sqlite3"
14
- gem "coveralls", :require=>false
15
- gem "ruby-prof", :platform=>:ruby
16
- gem "activerecord", "3.2.9", :require=>"active_record"
17
- gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby, :github=>"jruby/activerecord-jdbc-adapter"
18
-
19
- gemspec :path=>"../"