protector 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -0
- data/lib/protector/adapters/active_record/relation.rb +4 -0
- data/lib/protector/adapters/sequel/dataset.rb +4 -0
- data/lib/protector/dsl.rb +22 -9
- data/lib/protector/version.rb +1 -1
- data/lib/protector.rb +8 -1
- data/spec/lib/{adapters → protector/adapters}/active_record_spec.rb +5 -0
- data/spec/lib/{adapters → protector/adapters}/sequel_spec.rb +5 -0
- data/spec/lib/{dsl_spec.rb → protector/dsl_spec.rb} +26 -2
- data/spec/lib/{engine_spec.rb → protector/engine_spec.rb} +11 -6
- data/spec/spec_helpers/examples/model.rb +7 -1
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d22e142c0d5654a1287d706ad235b1d921fbf641
|
4
|
+
data.tar.gz: befe5711c88baea2c46f9004aaccfef20541db74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85757bb7c0f7e485d4912877572d0a9bfa7d890988c3c6df50bc40fb208e699f5394b3f014a675c7b24671aaad336b378c2d60197124efa4f0acc15024853fbe
|
7
|
+
data.tar.gz: 2c0c4d49b32b6d958804f1693bc10d018dba0b0566d0c7514fdc9ca974eec3351b89525986eb160f9562e95b1c5bd0bc8984334dde6a30548f05c3fda8d9cefc
|
data/README.md
CHANGED
@@ -175,6 +175,8 @@ end
|
|
175
175
|
|
176
176
|
No matter what happens inside, all your entities will act unprotected. So use with **EXTREME** caution.
|
177
177
|
|
178
|
+
Please note also that we are talking about "unprotected" and "disabled". It does not make `can?` to always return `true`. Instead `can?` would thrown an exception just like it does for any unprotected model. Any other approach makes logic incostitent, unpredictable and just dangerous. There are different possible strategies to isolate business logic from security domain in tests like direct `can?` mocking or forcing admin role to a test user. Use them whenever you want to abstract from security in a whole and `insecurely` when you want to mock a model to the basic security state.
|
179
|
+
|
178
180
|
## Ideology
|
179
181
|
|
180
182
|
Protector is a successor to [Heimdallr](https://github.com/inossidabile/heimdallr). The latter being a proof-of-concept appeared to be way too paranoid and incompatible with the rest of the world. Protector re-implements same idea keeping the Ruby way:
|
@@ -216,6 +218,11 @@ Use `Protector.config.option = value` to assign an option. Available options are
|
|
216
218
|
|
217
219
|
Protector features basic Rails integration so you can assign options using `config.protector.option = value` at your `config/*.rb`.
|
218
220
|
|
221
|
+
## Need help?
|
222
|
+
|
223
|
+
* Use [StackOverflow](http://stackoverflow.com/questions/tagged/protector) Luke! Make sure to use tag `protector`.
|
224
|
+
* You can get help at [irc.freenode.net](http://freenode.net) #protector.rb.
|
225
|
+
|
219
226
|
## Maintainers
|
220
227
|
|
221
228
|
* Boris Staal, [@inossidabile](http://staal.io)
|
@@ -30,6 +30,10 @@ module Protector
|
|
30
30
|
alias_method_chain :each, :protector
|
31
31
|
end
|
32
32
|
|
33
|
+
def creatable?
|
34
|
+
model.new.restrict!(protector_subject).creatable?
|
35
|
+
end
|
36
|
+
|
33
37
|
# Gets {Protector::DSL::Meta::Box} of this dataset
|
34
38
|
def protector_meta(subject=protector_subject)
|
35
39
|
model.protector_meta.evaluate(subject)
|
data/lib/protector/dsl.rb
CHANGED
@@ -16,7 +16,7 @@ module Protector
|
|
16
16
|
@adapter = adapter
|
17
17
|
@model = model
|
18
18
|
@fields = fields
|
19
|
-
@access = {
|
19
|
+
@access = {}
|
20
20
|
@scope_procs = []
|
21
21
|
@destroyable = false
|
22
22
|
|
@@ -104,9 +104,10 @@ module Protector
|
|
104
104
|
# end
|
105
105
|
def can(action, *fields)
|
106
106
|
return @destroyable = true if action == :destroy
|
107
|
+
|
107
108
|
@access[action] = {} unless @access[action]
|
108
109
|
|
109
|
-
if fields.
|
110
|
+
if fields.length == 0
|
110
111
|
@fields.each{|f| @access[action][f.to_s] = nil}
|
111
112
|
else
|
112
113
|
fields.each do |a|
|
@@ -132,10 +133,11 @@ module Protector
|
|
132
133
|
# @see #can?
|
133
134
|
def cannot(action, *fields)
|
134
135
|
return @destroyable = false if action == :destroy
|
136
|
+
|
135
137
|
return unless @access[action]
|
136
138
|
|
137
|
-
if fields.
|
138
|
-
@access
|
139
|
+
if fields.length == 0
|
140
|
+
@access.delete(action)
|
139
141
|
else
|
140
142
|
fields.each do |a|
|
141
143
|
if a.is_a?(Array)
|
@@ -144,6 +146,8 @@ module Protector
|
|
144
146
|
@access[action].delete(a.to_s)
|
145
147
|
end
|
146
148
|
end
|
149
|
+
|
150
|
+
@access.delete(action) if @access[action].empty?
|
147
151
|
end
|
148
152
|
end
|
149
153
|
|
@@ -151,7 +155,7 @@ module Protector
|
|
151
155
|
|
152
156
|
# Checks whether given field of a model is readable in context of current subject
|
153
157
|
def readable?(field)
|
154
|
-
@access[:view].has_key?(field)
|
158
|
+
@access[:view] && @access[:view].has_key?(field)
|
155
159
|
end
|
156
160
|
|
157
161
|
# Checks whether you can create a model with given field in context of current subject
|
@@ -182,14 +186,23 @@ module Protector
|
|
182
186
|
# @param [Symbol] action Action to check against
|
183
187
|
# @param [String] field Field to check against
|
184
188
|
def can?(action, field=false)
|
189
|
+
return destroyable? if action == :destroy
|
190
|
+
|
185
191
|
return false unless @access[action]
|
186
|
-
return !@access[action].empty?
|
192
|
+
return !@access[action].empty? unless field
|
193
|
+
|
187
194
|
@access[action].has_key?(field.to_s)
|
188
195
|
end
|
189
196
|
|
197
|
+
def cannot?(*args)
|
198
|
+
!can?(*args)
|
199
|
+
end
|
200
|
+
|
190
201
|
private
|
191
202
|
|
192
203
|
def first_unmodifiable_field(part, fields)
|
204
|
+
return (fields.keys.first || '-') unless @access[part]
|
205
|
+
|
193
206
|
diff = fields.keys - @access[part].keys
|
194
207
|
return diff.first if diff.length > 0
|
195
208
|
|
@@ -207,8 +220,8 @@ module Protector
|
|
207
220
|
false
|
208
221
|
end
|
209
222
|
|
210
|
-
def modifiable?(part, fields)
|
211
|
-
return false unless @access[part]
|
223
|
+
def modifiable?(part, fields=false)
|
224
|
+
return false unless @access[part]
|
212
225
|
return false if fields && first_unmodifiable_field(part, fields)
|
213
226
|
true
|
214
227
|
end
|
@@ -250,7 +263,7 @@ module Protector
|
|
250
263
|
# subject on a non-protected model
|
251
264
|
def protector_subject
|
252
265
|
unless protector_subject?
|
253
|
-
raise "Unprotected entity detected: use `restrict` method to protect it."
|
266
|
+
raise "Unprotected entity detected for '#{self.class}': use `restrict` method to protect it."
|
254
267
|
end
|
255
268
|
|
256
269
|
@protector_subject
|
data/lib/protector/version.rb
CHANGED
data/lib/protector.rb
CHANGED
@@ -25,10 +25,17 @@ module Protector
|
|
25
25
|
|
26
26
|
# Allows executing any code having Protector globally disabled
|
27
27
|
def insecurely(&block)
|
28
|
+
Thread.current[:protector_disabled_nesting] ||= 0
|
29
|
+
Thread.current[:protector_disabled_nesting] += 1
|
30
|
+
|
28
31
|
Thread.current[:protector_disabled] = true
|
29
32
|
yield
|
30
33
|
ensure
|
31
|
-
Thread.current[:
|
34
|
+
Thread.current[:protector_disabled_nesting] -= 1
|
35
|
+
|
36
|
+
if Thread.current[:protector_disabled_nesting] == 0
|
37
|
+
Thread.current[:protector_disabled] = false
|
38
|
+
end
|
32
39
|
end
|
33
40
|
|
34
41
|
def activate!
|
@@ -116,6 +116,11 @@ if defined?(ActiveRecord)
|
|
116
116
|
Dummy.restrict!('!').new.protector_subject.should == '!'
|
117
117
|
end
|
118
118
|
|
119
|
+
it "checks creatability" do
|
120
|
+
Dummy.restrict!('!').creatable?.should == false
|
121
|
+
Dummy.restrict!('!').where(number: 999).creatable?.should == false
|
122
|
+
end
|
123
|
+
|
119
124
|
context "with open relation" do
|
120
125
|
context "adequate", paranoid: false do
|
121
126
|
it "checks existence" do
|
@@ -88,6 +88,11 @@ if defined?(Sequel)
|
|
88
88
|
Dummy.restrict!('!').eager_graph(fluffies: :loony).all.first.fluffies.first.loony.protector_subject.should == '!'
|
89
89
|
end
|
90
90
|
|
91
|
+
it "checks creatability" do
|
92
|
+
Dummy.restrict!('!').creatable?.should == false
|
93
|
+
Dummy.restrict!('!').where(number: 999).creatable?.should == false
|
94
|
+
end
|
95
|
+
|
91
96
|
context "with open relation" do
|
92
97
|
context "adequate", paranoid: false do
|
93
98
|
it "checks existence" do
|
@@ -34,6 +34,28 @@ describe Protector::DSL do
|
|
34
34
|
base.unrestrict!
|
35
35
|
expect { base.protector_subject }.to raise_error
|
36
36
|
end
|
37
|
+
|
38
|
+
it "respects `insecurely`" do
|
39
|
+
base = @base.new
|
40
|
+
base.restrict!("universe")
|
41
|
+
|
42
|
+
base.protector_subject?.should == true
|
43
|
+
Protector.insecurely do
|
44
|
+
base.protector_subject?.should == false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it "allows nesting of `insecurely`" do
|
49
|
+
base = @base.new
|
50
|
+
base.restrict!("universe")
|
51
|
+
|
52
|
+
base.protector_subject?.should == true
|
53
|
+
Protector.insecurely do
|
54
|
+
Protector.insecurely do
|
55
|
+
base.protector_subject?.should == false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
37
59
|
end
|
38
60
|
|
39
61
|
describe Protector::DSL::Entry do
|
@@ -126,19 +148,20 @@ describe Protector::DSL do
|
|
126
148
|
"field1" => nil,
|
127
149
|
"field2" => nil,
|
128
150
|
"field3" => nil
|
129
|
-
}
|
130
|
-
create: {}
|
151
|
+
}
|
131
152
|
}
|
132
153
|
end
|
133
154
|
|
134
155
|
it "marks destroyable" do
|
135
156
|
data = @meta.evaluate('user', 'entry')
|
136
157
|
data.destroyable?.should == true
|
158
|
+
data.can?(:destroy).should == true
|
137
159
|
end
|
138
160
|
|
139
161
|
it "marks updatable" do
|
140
162
|
data = @meta.evaluate('user', 'entry')
|
141
163
|
data.updatable?.should == true
|
164
|
+
data.can?(:update).should == true
|
142
165
|
end
|
143
166
|
|
144
167
|
it "gets first unupdatable field" do
|
@@ -149,6 +172,7 @@ describe Protector::DSL do
|
|
149
172
|
it "marks creatable" do
|
150
173
|
data = @meta.evaluate('user', 'entry')
|
151
174
|
data.creatable?.should == false
|
175
|
+
data.can?(:create).should == false
|
152
176
|
end
|
153
177
|
|
154
178
|
it "gets first uncreatable field" do
|
@@ -28,8 +28,13 @@ if defined?(Rails)
|
|
28
28
|
describe "strong_parameters" do
|
29
29
|
before(:all) do
|
30
30
|
load 'migrations/active_record.rb'
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:dummy) do
|
34
|
+
Class.new(ActiveRecord::Base) do
|
35
|
+
def self.model_name; ActiveModel::Name.new(self, nil, "dummy"); end
|
36
|
+
self.table_name = "dummies"
|
31
37
|
|
32
|
-
Dummy.instance_eval do
|
33
38
|
protect do
|
34
39
|
can :create, :string
|
35
40
|
can :update, :number
|
@@ -42,15 +47,15 @@ if defined?(Rails)
|
|
42
47
|
end
|
43
48
|
|
44
49
|
it "creates" do
|
45
|
-
expect{
|
46
|
-
expect{
|
50
|
+
expect{ dummy.restrict!.new params(string: 'test') }.to_not raise_error
|
51
|
+
expect{ dummy.restrict!.new params(number: 1) }.to raise_error
|
47
52
|
end
|
48
53
|
|
49
54
|
it "updates" do
|
50
|
-
|
55
|
+
instance = dummy.create!
|
51
56
|
|
52
|
-
expect{
|
53
|
-
expect{
|
57
|
+
expect{ instance.restrict!.assign_attributes params(string: 'test') }.to raise_error
|
58
|
+
expect{ instance.restrict!.assign_attributes params(number: 1) }.to_not raise_error
|
54
59
|
end
|
55
60
|
end
|
56
61
|
end
|
@@ -33,7 +33,6 @@ shared_examples_for "a model" do
|
|
33
33
|
it "doesn't get stuck with non-existing tables" do
|
34
34
|
Rumba.class_eval do
|
35
35
|
protect do
|
36
|
-
can
|
37
36
|
end
|
38
37
|
end
|
39
38
|
end
|
@@ -88,6 +87,13 @@ shared_examples_for "a model" do
|
|
88
87
|
end
|
89
88
|
end
|
90
89
|
|
90
|
+
it "handles empty creations" do
|
91
|
+
d = dummy.new.restrict!('!')
|
92
|
+
d.can?(:create).should == false
|
93
|
+
d.creatable?.should == false
|
94
|
+
d.should invalidate
|
95
|
+
end
|
96
|
+
|
91
97
|
it "marks blocked" do
|
92
98
|
d = dummy.new(string: 'bam', number: 1)
|
93
99
|
d.restrict!('!').creatable?.should == false
|
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.6.
|
4
|
+
version: 0.6.2
|
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-
|
11
|
+
date: 2013-09-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -78,10 +78,10 @@ files:
|
|
78
78
|
- protector.gemspec
|
79
79
|
- spec/internal/config/database.yml
|
80
80
|
- spec/internal/db/schema.rb
|
81
|
-
- spec/lib/adapters/active_record_spec.rb
|
82
|
-
- spec/lib/adapters/sequel_spec.rb
|
83
|
-
- spec/lib/dsl_spec.rb
|
84
|
-
- spec/lib/engine_spec.rb
|
81
|
+
- spec/lib/protector/adapters/active_record_spec.rb
|
82
|
+
- spec/lib/protector/adapters/sequel_spec.rb
|
83
|
+
- spec/lib/protector/dsl_spec.rb
|
84
|
+
- spec/lib/protector/engine_spec.rb
|
85
85
|
- spec/spec_helpers/adapters/active_record.rb
|
86
86
|
- spec/spec_helpers/adapters/sequel.rb
|
87
87
|
- spec/spec_helpers/boot.rb
|
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
107
|
version: '0'
|
108
108
|
requirements: []
|
109
109
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.0.
|
110
|
+
rubygems_version: 2.0.3
|
111
111
|
signing_key:
|
112
112
|
specification_version: 4
|
113
113
|
summary: 'Protector is a successor to the Heimdallr gem: it hits the same goals keeping
|
@@ -115,10 +115,10 @@ summary: 'Protector is a successor to the Heimdallr gem: it hits the same goals
|
|
115
115
|
test_files:
|
116
116
|
- spec/internal/config/database.yml
|
117
117
|
- spec/internal/db/schema.rb
|
118
|
-
- spec/lib/adapters/active_record_spec.rb
|
119
|
-
- spec/lib/adapters/sequel_spec.rb
|
120
|
-
- spec/lib/dsl_spec.rb
|
121
|
-
- spec/lib/engine_spec.rb
|
118
|
+
- spec/lib/protector/adapters/active_record_spec.rb
|
119
|
+
- spec/lib/protector/adapters/sequel_spec.rb
|
120
|
+
- spec/lib/protector/dsl_spec.rb
|
121
|
+
- spec/lib/protector/engine_spec.rb
|
122
122
|
- spec/spec_helpers/adapters/active_record.rb
|
123
123
|
- spec/spec_helpers/adapters/sequel.rb
|
124
124
|
- spec/spec_helpers/boot.rb
|