protector 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Appraisals +1 -1
- data/README.md +22 -1
- data/gemfiles/AR_3.2.gemfile +1 -1
- data/gemfiles/AR_3.2.gemfile.lock +11 -25
- data/gemfiles/AR_4.gemfile.lock +1 -1
- data/lib/protector/adapters/active_record.rb +3 -1
- data/lib/protector/adapters/active_record/association.rb +6 -13
- data/lib/protector/adapters/active_record/base.rb +3 -2
- data/lib/protector/adapters/active_record/preloader.rb +32 -0
- data/lib/protector/adapters/active_record/relation.rb +132 -1
- data/lib/protector/dsl.rb +5 -1
- data/lib/protector/version.rb +1 -1
- data/spec/lib/adapters/active_record_spec.rb +123 -97
- data/spec/lib/dsl_spec.rb +6 -6
- data/spec/spec_helpers/model.rb +99 -113
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 70b9f27e548ca491e3d9e72033d82ccc6c1ef9a6
|
4
|
+
data.tar.gz: 174de9ede88655d225d7281e2d633cfd9c113cd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f86cb799de05ecb102b5c413492024be29a3d5b27a40e479c7e94e9afcfc7d47f8203e4e62d99f9ed406fa2943880acca7f79449341f3a6fc8ac480b8e59b59
|
7
|
+
data.tar.gz: 4864da9f17564ee319ed2faa7891a7a5e30f336bd7a9966ffda20aeae96337e02148ebf196bd0f504ad14f4487ff84b3ede8e7580a8e6f1ac045779ba33176c8
|
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Protector is a Ruby ORM extension for managing security restrictions on a field
|
|
7
7
|
|
8
8
|
Currently Protector supports the following ORM adapters:
|
9
9
|
|
10
|
-
* [ActiveRecord](http://guides.rubyonrails.org/active_record_querying.html) (>= 3.2)
|
10
|
+
* [ActiveRecord](http://guides.rubyonrails.org/active_record_querying.html) (>= 3.2.9)
|
11
11
|
|
12
12
|
We are working hard to extend the list with:
|
13
13
|
|
@@ -111,6 +111,27 @@ Protector is aware of associations. All the associations retrieved from restrict
|
|
111
111
|
|
112
112
|
The access to `belongs_to` kind of association depends on corresponding foreign key readability.
|
113
113
|
|
114
|
+
## Eager Loading
|
115
|
+
|
116
|
+
To take a long story short: it works and you are very likely to never notice changes it introduces to the process.
|
117
|
+
|
118
|
+
Eager Loading has 2 possible strategies: JOINs and additional requests. Whenever you mark an association to preload and at the same time use this relation among `where` clause – ORMs prefer JOIN. Otherwise it goes with additional requests.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
Foo.includes(:bars) # This will make 2 queries
|
122
|
+
Foo.includes(:bars).where(bars: {absolute: true}) # This will make 1 big JOINfull query
|
123
|
+
```
|
124
|
+
|
125
|
+
The problem here is that JOIN strategy is impossible for scoped restrictions. I.e. for the following code:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
Foo.restrict(current_user).includes(:bars).where(bars: {absolute: true})
|
129
|
+
```
|
130
|
+
|
131
|
+
we can appear in the situation where `foos` and `bars` relations are having different restrictions scopes. In this case JOIN would filter by an intersection of scopes which is wrong.
|
132
|
+
|
133
|
+
To solve the issue Protector forces additional requests strategy and intelligently adds proper JOINs to the general query to make your conditions work. That's why unlike unrestricted query from the first sample, the code from the second sample will result into 2 queries.
|
134
|
+
|
114
135
|
## Ideology
|
115
136
|
|
116
137
|
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:
|
data/gemfiles/AR_3.2.gemfile
CHANGED
@@ -10,7 +10,7 @@ gem "guard-rspec"
|
|
10
10
|
gem "appraisal"
|
11
11
|
gem "sqlite3", :platform=>:ruby
|
12
12
|
gem "jdbc-sqlite3", :platform=>:jruby
|
13
|
-
gem "activerecord", "3.2", :require=>"active_record"
|
13
|
+
gem "activerecord", "3.2.9", :require=>"active_record"
|
14
14
|
gem "activerecord-jdbcsqlite3-adapter", :platform=>:jruby, :github=>"jruby/activerecord-jdbc-adapter"
|
15
15
|
|
16
16
|
gemspec :path=>"../"
|
@@ -1,31 +1,27 @@
|
|
1
1
|
GIT
|
2
2
|
remote: git://github.com/jruby/activerecord-jdbc-adapter.git
|
3
|
-
revision:
|
3
|
+
revision: 180dd863d30d6048c914622e4e77f018cf77026a
|
4
4
|
specs:
|
5
|
-
activerecord-jdbcsqlite3-adapter (1.3.0.DEV)
|
6
|
-
activerecord-jdbc-adapter (~> 1.3.0.DEV)
|
7
|
-
jdbc-sqlite3 (~> 3.7.2)
|
8
5
|
|
9
6
|
PATH
|
10
7
|
remote: /Users/inossidabile/Repos/protector
|
11
8
|
specs:
|
12
|
-
protector (0.0
|
9
|
+
protector (0.1.0)
|
13
10
|
activesupport
|
14
11
|
i18n
|
15
12
|
|
16
13
|
GEM
|
17
14
|
remote: https://rubygems.org/
|
18
15
|
specs:
|
19
|
-
activemodel (3.2.
|
20
|
-
activesupport (= 3.2.
|
16
|
+
activemodel (3.2.9)
|
17
|
+
activesupport (= 3.2.9)
|
21
18
|
builder (~> 3.0.0)
|
22
|
-
activerecord (3.2.
|
23
|
-
activemodel (= 3.2.
|
24
|
-
activesupport (= 3.2.
|
25
|
-
arel (~> 3.0.
|
19
|
+
activerecord (3.2.9)
|
20
|
+
activemodel (= 3.2.9)
|
21
|
+
activesupport (= 3.2.9)
|
22
|
+
arel (~> 3.0.2)
|
26
23
|
tzinfo (~> 0.3.29)
|
27
|
-
|
28
|
-
activesupport (3.2.0)
|
24
|
+
activesupport (3.2.9)
|
29
25
|
i18n (~> 0.6)
|
30
26
|
multi_json (~> 1.0)
|
31
27
|
appraisal (0.5.2)
|
@@ -36,7 +32,6 @@ GEM
|
|
36
32
|
coderay (1.0.9)
|
37
33
|
diff-lcs (1.2.4)
|
38
34
|
ffi (1.8.1)
|
39
|
-
ffi (1.8.1-java)
|
40
35
|
formatador (0.2.4)
|
41
36
|
guard (1.8.0)
|
42
37
|
formatador (>= 0.2.4)
|
@@ -48,23 +43,17 @@ GEM
|
|
48
43
|
guard (>= 1.8)
|
49
44
|
rspec (~> 2.13)
|
50
45
|
i18n (0.6.4)
|
51
|
-
jdbc-sqlite3 (3.7.2)
|
52
46
|
listen (1.1.3)
|
53
47
|
rb-fsevent (>= 0.9.3)
|
54
48
|
rb-inotify (>= 0.9)
|
55
49
|
rb-kqueue (>= 0.2)
|
56
50
|
lumberjack (1.0.3)
|
57
51
|
method_source (0.8.1)
|
58
|
-
multi_json (1.7.
|
52
|
+
multi_json (1.7.4)
|
59
53
|
pry (0.9.12.2)
|
60
54
|
coderay (~> 1.0.5)
|
61
55
|
method_source (~> 0.8)
|
62
56
|
slop (~> 3.4)
|
63
|
-
pry (0.9.12.2-java)
|
64
|
-
coderay (~> 1.0.5)
|
65
|
-
method_source (~> 0.8)
|
66
|
-
slop (~> 3.4)
|
67
|
-
spoon (~> 0.0)
|
68
57
|
rake (10.0.4)
|
69
58
|
rb-fsevent (0.9.3)
|
70
59
|
rb-inotify (0.9.0)
|
@@ -80,18 +69,15 @@ GEM
|
|
80
69
|
diff-lcs (>= 1.1.3, < 2.0)
|
81
70
|
rspec-mocks (2.13.1)
|
82
71
|
slop (3.4.5)
|
83
|
-
spoon (0.0.4)
|
84
|
-
ffi
|
85
72
|
sqlite3 (1.3.7)
|
86
73
|
thor (0.18.1)
|
87
74
|
tzinfo (0.3.37)
|
88
75
|
|
89
76
|
PLATFORMS
|
90
|
-
java
|
91
77
|
ruby
|
92
78
|
|
93
79
|
DEPENDENCIES
|
94
|
-
activerecord (= 3.2)
|
80
|
+
activerecord (= 3.2.9)
|
95
81
|
activerecord-jdbcsqlite3-adapter!
|
96
82
|
appraisal
|
97
83
|
guard
|
data/gemfiles/AR_4.gemfile.lock
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'protector/adapters/active_record/base'
|
2
2
|
require 'protector/adapters/active_record/association'
|
3
3
|
require 'protector/adapters/active_record/relation'
|
4
|
+
require 'protector/adapters/active_record/preloader'
|
4
5
|
|
5
6
|
module Protector
|
6
7
|
module Adapters
|
@@ -10,7 +11,8 @@ module Protector
|
|
10
11
|
::ActiveRecord::Relation.send :include, Protector::Adapters::ActiveRecord::Relation
|
11
12
|
::ActiveRecord::Associations::SingularAssociation.send :include, Protector::Adapters::ActiveRecord::Association
|
12
13
|
::ActiveRecord::Associations::CollectionAssociation.send :include, Protector::Adapters::ActiveRecord::Association
|
13
|
-
|
14
|
+
::ActiveRecord::Associations::Preloader.send :include, Protector::Adapters::ActiveRecord::Preloader
|
15
|
+
::ActiveRecord::Associations::Preloader::Association.send :include, Protector::Adapters::ActiveRecord::Preloader::Association
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
@@ -5,25 +5,18 @@ module Protector
|
|
5
5
|
extend ActiveSupport::Concern
|
6
6
|
|
7
7
|
included do
|
8
|
-
|
8
|
+
if method_defined?(:scope)
|
9
|
+
alias_method_chain :scope, :protector
|
10
|
+
else
|
11
|
+
alias_method "scope_without_protector", "scoped"
|
12
|
+
alias_method "scoped", "scope_with_protector"
|
13
|
+
end
|
9
14
|
end
|
10
15
|
|
11
16
|
def scope_with_protector(*args)
|
12
17
|
scope_without_protector(*args).restrict!(owner.protector_subject)
|
13
18
|
end
|
14
19
|
end
|
15
|
-
|
16
|
-
module PreloaderAssociation
|
17
|
-
extend ActiveSupport::Concern
|
18
|
-
|
19
|
-
included do
|
20
|
-
alias_method_chain :scope, :protector
|
21
|
-
end
|
22
|
-
|
23
|
-
def scope_with_protector
|
24
|
-
scope_without_protector
|
25
|
-
end
|
26
|
-
end
|
27
20
|
end
|
28
21
|
end
|
29
22
|
end
|
@@ -46,7 +46,8 @@ module Protector
|
|
46
46
|
def define_method_attribute(name)
|
47
47
|
super
|
48
48
|
|
49
|
-
|
49
|
+
# Show some <3 to composite primary keys
|
50
|
+
unless (primary_key == name || Array(primary_key).include?(name))
|
50
51
|
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
51
52
|
alias_method #{"#{name}_unprotected".inspect}, #{name.inspect}
|
52
53
|
|
@@ -69,8 +70,8 @@ module Protector
|
|
69
70
|
|
70
71
|
self.class.protector_meta.evaluate(
|
71
72
|
self.class,
|
72
|
-
self.class.column_names,
|
73
73
|
@protector_subject,
|
74
|
+
self.class.column_names,
|
74
75
|
self
|
75
76
|
)
|
76
77
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Protector
|
2
|
+
module Adapters
|
3
|
+
module ActiveRecord
|
4
|
+
module Preloader extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module Association extend ActiveSupport::Concern
|
7
|
+
included do
|
8
|
+
if method_defined?(:scope)
|
9
|
+
alias_method_chain :scope, :protector
|
10
|
+
else
|
11
|
+
alias_method "scope_without_protector", "scoped"
|
12
|
+
alias_method "scoped", "scope_with_protector"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def protector_subject
|
17
|
+
# Owners are always loaded from the single source
|
18
|
+
# having same protector_subject
|
19
|
+
owners.first.protector_subject
|
20
|
+
end
|
21
|
+
|
22
|
+
def scope_with_protector(*args)
|
23
|
+
return scope_without_protector unless protector_subject
|
24
|
+
|
25
|
+
@meta ||= klass.protector_meta.evaluate(klass, protector_subject)
|
26
|
+
scope_without_protector.merge(@meta.relation)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,10 +8,29 @@ module Protector
|
|
8
8
|
include Protector::DSL::Base
|
9
9
|
|
10
10
|
alias_method_chain :exec_queries, :protector
|
11
|
+
alias_method_chain :eager_loading?, :protector
|
12
|
+
|
13
|
+
attr_accessor :eager_loadable_when_protected
|
14
|
+
|
15
|
+
# AR 3.2 workaround. Come on, guys... SQL parsing :(
|
16
|
+
unless method_defined?(:references_values)
|
17
|
+
def references_values
|
18
|
+
tables_in_string(to_sql)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
unless method_defined?(:includes!)
|
23
|
+
def includes!(*args)
|
24
|
+
self.includes_values += args
|
25
|
+
self
|
26
|
+
end
|
27
|
+
end
|
11
28
|
end
|
12
29
|
|
13
30
|
def protector_meta
|
14
|
-
|
31
|
+
# We don't seem to require columns here as well
|
32
|
+
# @klass.protector_meta.evaluate(@klass, @protector_subject, @klass.column_names)
|
33
|
+
@klass.protector_meta.evaluate(@klass, @protector_subject)
|
15
34
|
end
|
16
35
|
|
17
36
|
def unscoped
|
@@ -41,7 +60,119 @@ module Protector
|
|
41
60
|
|
42
61
|
subject = @protector_subject
|
43
62
|
relation = merge(protector_meta.relation).unrestrict!
|
63
|
+
|
64
|
+
relation = protector_substitute_includes(relation)
|
65
|
+
|
66
|
+
# We should explicitly allow/deny eager loading now that we know
|
67
|
+
# if we can use it
|
68
|
+
relation.eager_loadable_when_protected = relation.includes_values.any?
|
69
|
+
|
70
|
+
# Preserve associations from internal loading. We are going to handle that
|
71
|
+
# ourselves respecting security scopes FTW!
|
72
|
+
associations, relation.preload_values = relation.preload_values, []
|
73
|
+
|
44
74
|
@records = relation.send(:exec_queries).each{|r| r.restrict!(subject)}
|
75
|
+
|
76
|
+
# Now we have @records restricted properly so let's preload associations!
|
77
|
+
associations.each do |a|
|
78
|
+
::ActiveRecord::Associations::Preloader.new(@records, a).run
|
79
|
+
end
|
80
|
+
|
81
|
+
@loaded = true
|
82
|
+
@records
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# This method swaps `includes` with `preload` and adds JOINs
|
87
|
+
# to any table referenced from `where` (or manually with `reference`)
|
88
|
+
#
|
89
|
+
def protector_substitute_includes(relation)
|
90
|
+
subject = @protector_subject
|
91
|
+
includes, relation.includes_values = relation.includes_values, []
|
92
|
+
|
93
|
+
# We can not allow join-based eager loading for scoped associations
|
94
|
+
# since actual filtering can differ for host model and joined relation.
|
95
|
+
# Therefore we turn all `includes` into `preloads`.
|
96
|
+
#
|
97
|
+
# Note that `includes_values` shares reference across relation diffs so
|
98
|
+
# it has to be COPIED not modified
|
99
|
+
includes.each do |iv|
|
100
|
+
protector_expand_include(iv).each do |ive|
|
101
|
+
# First-level associations can stay JOINed if restriction scope
|
102
|
+
# is absent. Checking deep associations would make us to check
|
103
|
+
# every parent. This should probably be done sometimes :\
|
104
|
+
meta = ive[0].protector_meta.evaluate(ive[0], subject) unless ive[1].is_a?(Hash)
|
105
|
+
|
106
|
+
# We leave unscoped restrictions as `includes`
|
107
|
+
# but turn scoped ones into `preloads`
|
108
|
+
if meta && !meta.scoped?
|
109
|
+
relation.includes!(ive[1])
|
110
|
+
else
|
111
|
+
if relation.references_values.include?(ive[0].table_name)
|
112
|
+
if relation.respond_to?(:joins!)
|
113
|
+
relation.joins!(ive[1])
|
114
|
+
else
|
115
|
+
relation = relation.joins(ive[1])
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# AR 3.2 Y U NO HAVE BANG RELATION MODIFIERS
|
120
|
+
relation.preload_values << ive[1]
|
121
|
+
false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
relation
|
127
|
+
end
|
128
|
+
|
129
|
+
#
|
130
|
+
# Indexes `includes` format by actual entity class
|
131
|
+
# Turns {foo: :bar} into [[Foo, :foo], [Bar, {foo: :bar}]
|
132
|
+
#
|
133
|
+
def protector_expand_include(inclusion, results=[], base=[], klass=@klass)
|
134
|
+
if inclusion.is_a?(Hash)
|
135
|
+
protector_expand_include_hash(inclusion, results, base, klass)
|
136
|
+
else
|
137
|
+
Array(inclusion).each do |i|
|
138
|
+
if i.is_a?(Hash)
|
139
|
+
protector_expand_include_hash(i, results, base, klass)
|
140
|
+
else
|
141
|
+
results << [
|
142
|
+
klass.reflect_on_association(i.to_sym).klass,
|
143
|
+
i.to_sym
|
144
|
+
]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
results
|
150
|
+
end
|
151
|
+
|
152
|
+
def protector_expand_include_hash(inclusion, results=[], base=[], klass=@klass)
|
153
|
+
inclusion.each do |key, value|
|
154
|
+
model = klass.reflect_on_association(key.to_sym).klass
|
155
|
+
value = [value] unless value.is_a?(Array)
|
156
|
+
|
157
|
+
value.each do |v|
|
158
|
+
if v.is_a?(Hash)
|
159
|
+
protector_expand_include_hash(v, results, [key]+base)
|
160
|
+
else
|
161
|
+
results << [
|
162
|
+
model.reflect_on_association(v.to_sym).klass,
|
163
|
+
([key]+base).inject(v){|a, n| { n => a } }
|
164
|
+
]
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
results << [model, base.inject(key){|a, n| { n => a } }]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def eager_loading_with_protector?
|
173
|
+
flag = eager_loading_without_protector?
|
174
|
+
flag &&= !!@eager_loadable_when_protected unless @eager_loadable_when_protected.nil?
|
175
|
+
flag
|
45
176
|
end
|
46
177
|
end
|
47
178
|
end
|
data/lib/protector/dsl.rb
CHANGED
@@ -30,6 +30,10 @@ module Protector
|
|
30
30
|
@access.each{|k,v| @access_keys[k] = v.keys}
|
31
31
|
end
|
32
32
|
|
33
|
+
def scoped?
|
34
|
+
!!@relation
|
35
|
+
end
|
36
|
+
|
33
37
|
def scope(&block)
|
34
38
|
@relation = @model.instance_eval(&block)
|
35
39
|
end
|
@@ -112,7 +116,7 @@ module Protector
|
|
112
116
|
(@blocks ||= []) << block
|
113
117
|
end
|
114
118
|
|
115
|
-
def evaluate(model,
|
119
|
+
def evaluate(model, subject, fields=[], entry=nil)
|
116
120
|
Box.new(model, fields, subject, entry, @blocks)
|
117
121
|
end
|
118
122
|
end
|
data/lib/protector/version.rb
CHANGED
@@ -3,21 +3,29 @@ require 'spec_helpers/boot'
|
|
3
3
|
if defined?(ActiveRecord)
|
4
4
|
|
5
5
|
RSpec::Matchers.define :invalidate do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
match do |actual|
|
7
|
+
actual.save.should == false
|
8
|
+
actual.errors[:base].should == ["Access denied"]
|
9
|
+
end
|
10
10
|
end
|
11
11
|
|
12
12
|
RSpec::Matchers.define :validate do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
match do |actual|
|
14
|
+
actual.class.transaction do
|
15
|
+
actual.save.should == true
|
16
|
+
raise ActiveRecord::Rollback
|
17
|
+
end
|
18
18
|
|
19
|
-
|
19
|
+
true
|
20
|
+
end
|
20
21
|
end
|
22
|
+
|
23
|
+
def log!
|
24
|
+
around(:each) do |e|
|
25
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
26
|
+
e.run
|
27
|
+
ActiveRecord::Base.logger = nil
|
28
|
+
end
|
21
29
|
end
|
22
30
|
|
23
31
|
describe Protector::Adapters::ActiveRecord do
|
@@ -25,67 +33,82 @@ if defined?(ActiveRecord)
|
|
25
33
|
ActiveRecord::Schema.verbose = false
|
26
34
|
ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
|
27
35
|
|
28
|
-
|
29
|
-
t
|
30
|
-
|
31
|
-
|
32
|
-
|
36
|
+
[:dummies, :fluffies, :bobbies].each do |m|
|
37
|
+
ActiveRecord::Migration.create_table m do |t|
|
38
|
+
t.string :string
|
39
|
+
t.integer :number
|
40
|
+
t.text :text
|
41
|
+
t.belongs_to :dummy
|
42
|
+
t.timestamps
|
43
|
+
end
|
33
44
|
end
|
34
45
|
|
35
|
-
ActiveRecord::Migration.create_table :
|
36
|
-
t.string :string
|
37
|
-
t.integer :number
|
38
|
-
t.belongs_to :dummy
|
39
|
-
t.timestamps
|
40
|
-
end
|
46
|
+
ActiveRecord::Migration.create_table(:loonies){|t| t.belongs_to :fluffy; t.string :string }
|
41
47
|
|
42
48
|
Protector::Adapters::ActiveRecord.activate!
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
50
|
+
module Tester extend ActiveSupport::Concern
|
51
|
+
included do
|
52
|
+
protect do |x|
|
53
|
+
scope{ where('1=0') } if x == '-'
|
54
|
+
scope{ where(number: 999) } if x == '+'
|
55
|
+
|
56
|
+
can :view, :dummy_id unless x == '-'
|
57
|
+
end
|
58
|
+
|
59
|
+
scope :none, where('1 = 0') unless respond_to?(:none)
|
48
60
|
end
|
61
|
+
end
|
49
62
|
|
50
|
-
|
63
|
+
class Dummy < ActiveRecord::Base
|
64
|
+
include Tester
|
51
65
|
has_many :fluffies
|
66
|
+
has_many :bobbies
|
52
67
|
end
|
53
|
-
Dummy.create! string: 'zomgstring', number: 999, text: 'zomgtext'
|
54
|
-
Dummy.create! string: 'zomgstring', number: 777, text: 'zomgtext'
|
55
68
|
|
56
69
|
class Fluffy < ActiveRecord::Base
|
57
|
-
|
58
|
-
|
59
|
-
|
70
|
+
include Tester
|
71
|
+
belongs_to :dummy
|
72
|
+
has_one :loony
|
73
|
+
end
|
60
74
|
|
61
|
-
|
62
|
-
end
|
75
|
+
class Bobby < ActiveRecord::Base
|
76
|
+
protect do; end
|
77
|
+
end
|
63
78
|
|
64
|
-
|
65
|
-
|
79
|
+
class Loony < ActiveRecord::Base
|
80
|
+
protect do; end
|
81
|
+
end
|
82
|
+
|
83
|
+
Dummy.create! string: 'zomgstring', number: 999, text: 'zomgtext'
|
84
|
+
Dummy.create! string: 'zomgstring', number: 999, text: 'zomgtext'
|
85
|
+
Dummy.create! string: 'zomgstring', number: 777, text: 'zomgtext'
|
86
|
+
Dummy.create! string: 'zomgstring', number: 777, text: 'zomgtext'
|
87
|
+
|
88
|
+
[Fluffy, Bobby].each do |m|
|
89
|
+
m.create! string: 'zomgstring', number: 999, text: 'zomgtext', dummy_id: 1
|
90
|
+
m.create! string: 'zomgstring', number: 777, text: 'zomgtext', dummy_id: 1
|
91
|
+
m.create! string: 'zomgstring', number: 999, text: 'zomgtext', dummy_id: 2
|
92
|
+
m.create! string: 'zomgstring', number: 777, text: 'zomgtext', dummy_id: 2
|
66
93
|
end
|
67
|
-
|
68
|
-
Fluffy.create! string: 'zomgstring'
|
94
|
+
|
95
|
+
Fluffy.all.each{|f| Loony.create! fluffy_id: f.id, string: 'zomgstring' }
|
69
96
|
end
|
70
97
|
|
71
98
|
describe Protector::Adapters::ActiveRecord::Base do
|
72
|
-
|
73
|
-
|
99
|
+
let(:dummy) do
|
100
|
+
Class.new(ActiveRecord::Base) do
|
74
101
|
self.table_name = "dummies"
|
75
102
|
scope :none, where('1 = 0') unless respond_to?(:none)
|
76
103
|
end
|
77
104
|
end
|
78
105
|
|
79
106
|
it "includes" do
|
80
|
-
|
107
|
+
Dummy.ancestors.should include(Protector::Adapters::ActiveRecord::Base)
|
81
108
|
end
|
82
109
|
|
83
110
|
it "scopes" do
|
84
|
-
|
85
|
-
protect do; scope{ all }; end
|
86
|
-
end
|
87
|
-
|
88
|
-
scope = @dummy.restrict!('!')
|
111
|
+
scope = Dummy.restrict!('!')
|
89
112
|
scope.should be_a_kind_of ActiveRecord::Relation
|
90
113
|
scope.protector_subject.should == '!'
|
91
114
|
end
|
@@ -93,101 +116,104 @@ if defined?(ActiveRecord)
|
|
93
116
|
it_behaves_like "a model"
|
94
117
|
|
95
118
|
describe "eager loading" do
|
96
|
-
# around(:each) do |e|
|
97
|
-
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
98
|
-
# e.run
|
99
|
-
# ActiveRecord::Base.logger = nil
|
100
|
-
# end
|
101
|
-
|
102
119
|
it "scopes" do
|
103
|
-
|
104
|
-
|
120
|
+
d = Dummy.restrict!('+').includes(:fluffies)
|
121
|
+
d.length.should == 2
|
122
|
+
d.first.fluffies.length.should == 1
|
105
123
|
end
|
106
|
-
end
|
107
|
-
end
|
108
124
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
125
|
+
context "joined to filtered association" do
|
126
|
+
it "scopes" do
|
127
|
+
d = Dummy.restrict!('+').includes(:fluffies).where(fluffies: {number: 777})
|
128
|
+
d.length.should == 2
|
129
|
+
d.first.fluffies.length.should == 1
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "joined to plain association" do
|
134
|
+
it "scopes" do
|
135
|
+
d = Dummy.restrict!('+').includes(:bobbies, :fluffies).where(
|
136
|
+
bobbies: {number: 777}, fluffies: {number: 777}
|
137
|
+
)
|
138
|
+
d.length.should == 2
|
139
|
+
d.first.fluffies.length.should == 1
|
140
|
+
d.first.bobbies.length.should == 1
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context "with complex include" do
|
145
|
+
it "scopes" do
|
146
|
+
d = Dummy.restrict!('+').includes(fluffies: :loony).where(
|
147
|
+
fluffies: {number: 777},
|
148
|
+
loonies: {string: 'zomgstring'}
|
149
|
+
)
|
150
|
+
d.length.should == 2
|
151
|
+
d.first.fluffies.length.should == 1
|
152
|
+
d.first.fluffies.first.loony.should be_a_kind_of(Loony)
|
153
|
+
end
|
114
154
|
end
|
115
155
|
end
|
156
|
+
end
|
116
157
|
|
158
|
+
describe Protector::Adapters::ActiveRecord::Relation do
|
117
159
|
it "includes" do
|
118
|
-
|
160
|
+
Dummy.none.ancestors.should include(Protector::Adapters::ActiveRecord::Base)
|
119
161
|
end
|
120
162
|
|
121
163
|
it "saves subject" do
|
122
|
-
|
164
|
+
Dummy.restrict!('!').where(number: 999).protector_subject.should == '!'
|
123
165
|
end
|
124
166
|
|
125
167
|
it "forwards subject" do
|
126
|
-
|
127
|
-
|
128
|
-
end
|
129
|
-
|
130
|
-
@dummy.restrict!('!').where(number: 999).first.protector_subject.should == '!'
|
131
|
-
@dummy.restrict!('!').where(number: 999).to_a.first.protector_subject.should == '!'
|
168
|
+
Dummy.restrict!('!').where(number: 999).first.protector_subject.should == '!'
|
169
|
+
Dummy.restrict!('!').where(number: 999).to_a.first.protector_subject.should == '!'
|
132
170
|
end
|
133
171
|
|
134
172
|
context "with null relation" do
|
135
|
-
before(:each) do
|
136
|
-
@dummy.instance_eval do
|
137
|
-
protect do; scope{ none }; end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
173
|
it "checks existence" do
|
142
|
-
|
143
|
-
|
174
|
+
Dummy.any?.should == true
|
175
|
+
Dummy.restrict!('-').any?.should == false
|
144
176
|
end
|
145
177
|
|
146
178
|
it "counts" do
|
147
|
-
|
148
|
-
|
179
|
+
Dummy.count.should == 4
|
180
|
+
Dummy.restrict!('-').count.should == 0
|
149
181
|
end
|
150
182
|
|
151
183
|
it "fetches" do
|
152
|
-
fetched =
|
184
|
+
fetched = Dummy.restrict!('-').to_a
|
153
185
|
|
154
|
-
|
186
|
+
Dummy.count.should == 4
|
155
187
|
fetched.length.should == 0
|
156
188
|
end
|
157
189
|
|
158
190
|
it "keeps security scope when unscoped" do
|
159
|
-
|
160
|
-
|
191
|
+
Dummy.unscoped.restrict!('-').count.should == 0
|
192
|
+
Dummy.restrict!('-').unscoped.count.should == 0
|
161
193
|
end
|
162
194
|
end
|
163
195
|
|
164
196
|
context "with active relation" do
|
165
|
-
before(:each) do
|
166
|
-
@dummy.instance_eval do
|
167
|
-
protect do; scope{ where(number: 999) }; end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
197
|
it "checks existence" do
|
172
|
-
|
173
|
-
|
198
|
+
Dummy.any?.should == true
|
199
|
+
Dummy.restrict!('+').any?.should == true
|
174
200
|
end
|
175
201
|
|
176
202
|
it "counts" do
|
177
|
-
|
178
|
-
|
203
|
+
Dummy.count.should == 4
|
204
|
+
Dummy.restrict!('+').count.should == 2
|
179
205
|
end
|
180
206
|
|
181
207
|
it "fetches" do
|
182
|
-
fetched =
|
208
|
+
fetched = Dummy.restrict!('+').to_a
|
183
209
|
|
184
|
-
|
185
|
-
fetched.length.should ==
|
210
|
+
Dummy.count.should == 4
|
211
|
+
fetched.length.should == 2
|
186
212
|
end
|
187
213
|
|
188
214
|
it "keeps security scope when unscoped" do
|
189
|
-
|
190
|
-
|
215
|
+
Dummy.unscoped.restrict!('+').count.should == 2
|
216
|
+
Dummy.restrict!('+').unscoped.count.should == 2
|
191
217
|
end
|
192
218
|
end
|
193
219
|
end
|
data/spec/lib/dsl_spec.rb
CHANGED
@@ -70,16 +70,16 @@ describe Protector::DSL do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
it "evaluates" do
|
73
|
-
@meta.evaluate(nil,
|
73
|
+
@meta.evaluate(nil, 'user', [], 'entry')
|
74
74
|
end
|
75
75
|
|
76
76
|
it "sets relation" do
|
77
|
-
data = @meta.evaluate(nil,
|
77
|
+
data = @meta.evaluate(nil, 'user', [], 'entry')
|
78
78
|
data.relation.should == 'relation'
|
79
79
|
end
|
80
80
|
|
81
81
|
it "sets access" do
|
82
|
-
data = @meta.evaluate(nil, %w(field1 field2 field3 field4 field5), '
|
82
|
+
data = @meta.evaluate(nil, 'user', %w(field1 field2 field3 field4 field5), 'entry')
|
83
83
|
data.access.should == {
|
84
84
|
"update" => {
|
85
85
|
"field1" => nil,
|
@@ -98,17 +98,17 @@ describe Protector::DSL do
|
|
98
98
|
end
|
99
99
|
|
100
100
|
it "marks destroyable" do
|
101
|
-
data = @meta.evaluate(nil,
|
101
|
+
data = @meta.evaluate(nil, 'user', [], 'entry')
|
102
102
|
data.destroyable?.should == true
|
103
103
|
end
|
104
104
|
|
105
105
|
it "marks updatable" do
|
106
|
-
data = @meta.evaluate(nil,
|
106
|
+
data = @meta.evaluate(nil, 'user', [], 'entry')
|
107
107
|
data.updatable?.should == true
|
108
108
|
end
|
109
109
|
|
110
110
|
it "marks creatable" do
|
111
|
-
data = @meta.evaluate(nil,
|
111
|
+
data = @meta.evaluate(nil, 'user', [], 'entry')
|
112
112
|
data.creatable?.should == false
|
113
113
|
end
|
114
114
|
end
|
data/spec/spec_helpers/model.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
shared_examples_for "a model" do
|
2
2
|
it "evaluates meta properly" do
|
3
|
-
|
3
|
+
dummy.instance_eval do
|
4
4
|
protect do |subject, dummy|
|
5
5
|
subject.should == '!'
|
6
6
|
|
@@ -12,9 +12,8 @@ shared_examples_for "a model" do
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
fields = Hash[*%w(id string number text created_at updated_at).map{|x| [x, nil]}.flatten]
|
16
|
-
|
17
|
-
meta = dummy.protector_meta
|
15
|
+
fields = Hash[*%w(id string number text dummy_id created_at updated_at).map{|x| [x, nil]}.flatten]
|
16
|
+
meta = dummy.new.restrict!('!').protector_meta
|
18
17
|
|
19
18
|
meta.access[:view].should == fields
|
20
19
|
meta.access[:create].should == fields
|
@@ -23,11 +22,6 @@ shared_examples_for "a model" do
|
|
23
22
|
|
24
23
|
describe "association" do
|
25
24
|
context "(has_many)" do
|
26
|
-
around(:each) do |e|
|
27
|
-
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
28
|
-
e.run
|
29
|
-
ActiveRecord::Base.logger = nil
|
30
|
-
end
|
31
25
|
it "loads" do
|
32
26
|
Dummy.first.restrict!('!').fluffies.length.should == 2
|
33
27
|
Dummy.first.restrict!('+').fluffies.length.should == 1
|
@@ -49,60 +43,52 @@ shared_examples_for "a model" do
|
|
49
43
|
|
50
44
|
describe "visibility" do
|
51
45
|
it "marks blocked" do
|
52
|
-
|
53
|
-
protect do; scope { none }; end
|
54
|
-
end
|
55
|
-
|
56
|
-
@dummy.first.restrict!('!').visible?.should == false
|
46
|
+
Dummy.first.restrict!('-').visible?.should == false
|
57
47
|
end
|
58
48
|
|
59
49
|
it "marks allowed" do
|
60
|
-
|
61
|
-
protect do; scope { limit(5) }; end
|
62
|
-
end
|
63
|
-
|
64
|
-
@dummy.first.restrict!('!').visible?.should == true
|
50
|
+
Dummy.first.restrict!('+').visible?.should == true
|
65
51
|
end
|
66
52
|
end
|
67
53
|
|
68
54
|
describe "readability" do
|
69
55
|
it "hides fields" do
|
70
|
-
|
56
|
+
dummy.instance_eval do
|
71
57
|
protect do
|
72
58
|
can :view, :string
|
73
59
|
end
|
74
60
|
end
|
75
61
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
62
|
+
d = dummy.first.restrict!('!')
|
63
|
+
d.number.should == nil
|
64
|
+
d[:number].should == nil
|
65
|
+
d.read_attribute(:number).should_not == nil
|
66
|
+
d.string.should == 'zomgstring'
|
81
67
|
end
|
82
68
|
end
|
83
69
|
|
84
70
|
describe "creatability" do
|
85
71
|
context "with empty meta" do
|
86
72
|
before(:each) do
|
87
|
-
|
73
|
+
dummy.instance_eval do
|
88
74
|
protect do; end
|
89
75
|
end
|
90
76
|
end
|
91
77
|
|
92
78
|
it "marks blocked" do
|
93
|
-
|
94
|
-
|
79
|
+
d = dummy.new(string: 'bam', number: 1)
|
80
|
+
d.restrict!('!').creatable?.should == false
|
95
81
|
end
|
96
82
|
|
97
83
|
it "invalidates" do
|
98
|
-
|
99
|
-
|
84
|
+
d = dummy.new(string: 'bam', number: 1).restrict!('!')
|
85
|
+
d.should invalidate
|
100
86
|
end
|
101
87
|
end
|
102
88
|
|
103
89
|
context "by list of fields" do
|
104
90
|
before(:each) do
|
105
|
-
|
91
|
+
dummy.instance_eval do
|
106
92
|
protect do
|
107
93
|
can :create, :string
|
108
94
|
end
|
@@ -110,29 +96,29 @@ shared_examples_for "a model" do
|
|
110
96
|
end
|
111
97
|
|
112
98
|
it "marks blocked" do
|
113
|
-
|
114
|
-
|
99
|
+
d = dummy.new(string: 'bam', number: 1)
|
100
|
+
d.restrict!('!').creatable?.should == false
|
115
101
|
end
|
116
102
|
|
117
103
|
it "marks allowed" do
|
118
|
-
|
119
|
-
|
104
|
+
d = dummy.new(string: 'bam')
|
105
|
+
d.restrict!('!').creatable?.should == true
|
120
106
|
end
|
121
107
|
|
122
108
|
it "invalidates" do
|
123
|
-
|
124
|
-
|
109
|
+
d = dummy.new(string: 'bam', number: 1).restrict!('!')
|
110
|
+
d.should invalidate
|
125
111
|
end
|
126
112
|
|
127
113
|
it "validates" do
|
128
|
-
|
129
|
-
|
114
|
+
d = dummy.new(string: 'bam').restrict!('!')
|
115
|
+
d.should validate
|
130
116
|
end
|
131
117
|
end
|
132
118
|
|
133
119
|
context "by lambdas" do
|
134
120
|
before(:each) do
|
135
|
-
|
121
|
+
dummy.instance_eval do
|
136
122
|
protect do
|
137
123
|
can :create, string: lambda {|x| x.try(:length) == 5 }
|
138
124
|
end
|
@@ -140,29 +126,29 @@ shared_examples_for "a model" do
|
|
140
126
|
end
|
141
127
|
|
142
128
|
it "marks blocked" do
|
143
|
-
|
144
|
-
|
129
|
+
d = dummy.new(string: 'bam')
|
130
|
+
d.restrict!('!').creatable?.should == false
|
145
131
|
end
|
146
132
|
|
147
133
|
it "marks allowed" do
|
148
|
-
|
149
|
-
|
134
|
+
d = dummy.new(string: '12345')
|
135
|
+
d.restrict!('!').creatable?.should == true
|
150
136
|
end
|
151
137
|
|
152
138
|
it "invalidates" do
|
153
|
-
|
154
|
-
|
139
|
+
d = dummy.new(string: 'bam').restrict!('!')
|
140
|
+
d.should invalidate
|
155
141
|
end
|
156
142
|
|
157
143
|
it "validates" do
|
158
|
-
|
159
|
-
|
144
|
+
d = dummy.new(string: '12345').restrict!('!')
|
145
|
+
d.should validate
|
160
146
|
end
|
161
147
|
end
|
162
148
|
|
163
149
|
context "by ranges" do
|
164
150
|
before(:each) do
|
165
|
-
|
151
|
+
dummy.instance_eval do
|
166
152
|
protect do
|
167
153
|
can :create, number: 0..2
|
168
154
|
end
|
@@ -170,23 +156,23 @@ shared_examples_for "a model" do
|
|
170
156
|
end
|
171
157
|
|
172
158
|
it "marks blocked" do
|
173
|
-
|
174
|
-
|
159
|
+
d = dummy.new(number: 500)
|
160
|
+
d.restrict!('!').creatable?.should == false
|
175
161
|
end
|
176
162
|
|
177
163
|
it "marks allowed" do
|
178
|
-
|
179
|
-
|
164
|
+
d = dummy.new(number: 2)
|
165
|
+
d.restrict!('!').creatable?.should == true
|
180
166
|
end
|
181
167
|
|
182
168
|
it "invalidates" do
|
183
|
-
|
184
|
-
|
169
|
+
d = dummy.new(number: 500).restrict!('!')
|
170
|
+
d.should invalidate
|
185
171
|
end
|
186
172
|
|
187
173
|
it "validates" do
|
188
|
-
|
189
|
-
|
174
|
+
d = dummy.new(number: 2).restrict!('!')
|
175
|
+
d.should validate
|
190
176
|
end
|
191
177
|
end
|
192
178
|
end
|
@@ -194,27 +180,27 @@ shared_examples_for "a model" do
|
|
194
180
|
describe "updatability" do
|
195
181
|
context "with empty meta" do
|
196
182
|
before(:each) do
|
197
|
-
|
183
|
+
dummy.instance_eval do
|
198
184
|
protect do; end
|
199
185
|
end
|
200
186
|
end
|
201
187
|
|
202
188
|
it "marks blocked" do
|
203
|
-
|
204
|
-
|
205
|
-
|
189
|
+
d = dummy.first
|
190
|
+
d.assign_attributes(string: 'bam', number: 1)
|
191
|
+
d.restrict!('!').updatable?.should == false
|
206
192
|
end
|
207
193
|
|
208
194
|
it "invalidates" do
|
209
|
-
|
210
|
-
|
211
|
-
|
195
|
+
d = dummy.first.restrict!('!')
|
196
|
+
d.assign_attributes(string: 'bam', number: 1)
|
197
|
+
d.should invalidate
|
212
198
|
end
|
213
199
|
end
|
214
200
|
|
215
201
|
context "by list of fields" do
|
216
202
|
before(:each) do
|
217
|
-
|
203
|
+
dummy.instance_eval do
|
218
204
|
protect do
|
219
205
|
can :update, :string
|
220
206
|
end
|
@@ -222,33 +208,33 @@ shared_examples_for "a model" do
|
|
222
208
|
end
|
223
209
|
|
224
210
|
it "marks blocked" do
|
225
|
-
|
226
|
-
|
227
|
-
|
211
|
+
d = dummy.first
|
212
|
+
d.assign_attributes(string: 'bam', number: 1)
|
213
|
+
d.restrict!('!').updatable?.should == false
|
228
214
|
end
|
229
215
|
|
230
216
|
it "marks allowed" do
|
231
|
-
|
232
|
-
|
233
|
-
|
217
|
+
d = dummy.first
|
218
|
+
d.assign_attributes(string: 'bam')
|
219
|
+
d.restrict!('!').updatable?.should == true
|
234
220
|
end
|
235
221
|
|
236
222
|
it "invalidates" do
|
237
|
-
|
238
|
-
|
239
|
-
|
223
|
+
d = dummy.first.restrict!('!')
|
224
|
+
d.assign_attributes(string: 'bam', number: 1)
|
225
|
+
d.should invalidate
|
240
226
|
end
|
241
227
|
|
242
228
|
it "validates" do
|
243
|
-
|
244
|
-
|
245
|
-
|
229
|
+
d = dummy.first.restrict!('!')
|
230
|
+
d.assign_attributes(string: 'bam')
|
231
|
+
d.should validate
|
246
232
|
end
|
247
233
|
end
|
248
234
|
|
249
235
|
context "by lambdas" do
|
250
236
|
before(:each) do
|
251
|
-
|
237
|
+
dummy.instance_eval do
|
252
238
|
protect do
|
253
239
|
can :update, string: lambda {|x| x.try(:length) == 5 }
|
254
240
|
end
|
@@ -256,33 +242,33 @@ shared_examples_for "a model" do
|
|
256
242
|
end
|
257
243
|
|
258
244
|
it "marks blocked" do
|
259
|
-
|
260
|
-
|
261
|
-
|
245
|
+
d = dummy.first
|
246
|
+
d.assign_attributes(string: 'bam')
|
247
|
+
d.restrict!('!').updatable?.should == false
|
262
248
|
end
|
263
249
|
|
264
250
|
it "marks allowed" do
|
265
|
-
|
266
|
-
|
267
|
-
|
251
|
+
d = dummy.first
|
252
|
+
d.assign_attributes(string: '12345')
|
253
|
+
d.restrict!('!').updatable?.should == true
|
268
254
|
end
|
269
255
|
|
270
256
|
it "invalidates" do
|
271
|
-
|
272
|
-
|
273
|
-
|
257
|
+
d = dummy.first.restrict!('!')
|
258
|
+
d.assign_attributes(string: 'bam')
|
259
|
+
d.should invalidate
|
274
260
|
end
|
275
261
|
|
276
262
|
it "validates" do
|
277
|
-
|
278
|
-
|
279
|
-
|
263
|
+
d = dummy.first.restrict!('!')
|
264
|
+
d.assign_attributes(string: '12345')
|
265
|
+
d.should validate
|
280
266
|
end
|
281
267
|
end
|
282
268
|
|
283
269
|
context "by ranges" do
|
284
270
|
before(:each) do
|
285
|
-
|
271
|
+
dummy.instance_eval do
|
286
272
|
protect do
|
287
273
|
can :update, number: 0..2
|
288
274
|
end
|
@@ -290,64 +276,64 @@ shared_examples_for "a model" do
|
|
290
276
|
end
|
291
277
|
|
292
278
|
it "marks blocked" do
|
293
|
-
|
294
|
-
|
295
|
-
|
279
|
+
d = dummy.first
|
280
|
+
d.assign_attributes(number: 500)
|
281
|
+
d.restrict!('!').updatable?.should == false
|
296
282
|
end
|
297
283
|
|
298
284
|
it "marks allowed" do
|
299
|
-
|
300
|
-
|
301
|
-
|
285
|
+
d = dummy.first
|
286
|
+
d.assign_attributes(number: 2)
|
287
|
+
d.restrict!('!').updatable?.should == true
|
302
288
|
end
|
303
289
|
|
304
290
|
it "invalidates" do
|
305
|
-
|
306
|
-
|
307
|
-
|
291
|
+
d = dummy.first.restrict!('!')
|
292
|
+
d.assign_attributes(number: 500)
|
293
|
+
d.should invalidate
|
308
294
|
end
|
309
295
|
|
310
296
|
it "validates" do
|
311
|
-
|
312
|
-
|
313
|
-
|
297
|
+
d = dummy.first.restrict!('!')
|
298
|
+
d.assign_attributes(number: 2)
|
299
|
+
d.should validate
|
314
300
|
end
|
315
301
|
end
|
316
302
|
end
|
317
303
|
|
318
304
|
describe "destroyability" do
|
319
305
|
it "marks blocked" do
|
320
|
-
|
306
|
+
dummy.instance_eval do
|
321
307
|
protect do; end
|
322
308
|
end
|
323
309
|
|
324
|
-
|
310
|
+
dummy.first.restrict!('!').destroyable?.should == false
|
325
311
|
end
|
326
312
|
|
327
313
|
it "marks allowed" do
|
328
|
-
|
314
|
+
dummy.instance_eval do
|
329
315
|
protect do; can :destroy; end
|
330
316
|
end
|
331
317
|
|
332
|
-
|
318
|
+
dummy.first.restrict!('!').destroyable?.should == true
|
333
319
|
end
|
334
320
|
|
335
321
|
it "invalidates" do
|
336
|
-
|
322
|
+
dummy.instance_eval do
|
337
323
|
protect do; end
|
338
324
|
end
|
339
325
|
|
340
|
-
|
326
|
+
dummy.first.restrict!('!').destroy.should == false
|
341
327
|
end
|
342
328
|
|
343
329
|
it "validates" do
|
344
|
-
|
330
|
+
dummy.instance_eval do
|
345
331
|
protect do; can :destroy; end
|
346
332
|
end
|
347
333
|
|
348
|
-
|
349
|
-
|
350
|
-
|
334
|
+
d = dummy.create!.restrict!('!')
|
335
|
+
d.destroy.should == d
|
336
|
+
d.destroyed?.should == true
|
351
337
|
end
|
352
338
|
end
|
353
339
|
end
|
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.0
|
4
|
+
version: 0.1.0
|
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-05-
|
11
|
+
date: 2013-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- lib/protector/adapters/active_record.rb
|
63
63
|
- lib/protector/adapters/active_record/association.rb
|
64
64
|
- lib/protector/adapters/active_record/base.rb
|
65
|
+
- lib/protector/adapters/active_record/preloader.rb
|
65
66
|
- lib/protector/adapters/active_record/relation.rb
|
66
67
|
- lib/protector/dsl.rb
|
67
68
|
- lib/protector/version.rb
|