controlist 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 224e42ca7d06b607134503d787c8e37b4960f936
4
- data.tar.gz: 0bfc1f31babcb7d2560d331321388480b5dbc207
3
+ metadata.gz: 641ff1fb7c170c5e3d33641eebb63e8d6ab42fcc
4
+ data.tar.gz: beb6c94f12192612b39df5b868ecc77a999eef68
5
5
  SHA512:
6
- metadata.gz: e5283e1ddeb54abf1618664170e0262e0fe97ce30d38c7bd6e4d49c6b4378e2866ecca5d134cd815ad481c02ba36a9857ea90deb2e75cf2e62c204d121cecefe
7
- data.tar.gz: 2b5d09192b442afbcd3c0c86cbc4584e3f781b79d7180722f4f275c5f17e6e05b0eb38a3c78d6751eebda873e1719ce9d4c371a78a4f865b37782bb4dd7e23d9
6
+ metadata.gz: 65809c604627384c839c55d55eae91dab922cb6c95999d129bf196647073d0bb5e5c4dd009708f4f5620ce5465d8ca5d3d54c832a5a6a4d07dd004ce47f7704a
7
+ data.tar.gz: 86817ef4ae15ce9409f099c54a8cf0913c4a2ff6d063fa85c7e3e86535e7005969acad42eda4e8e67e21abfffecb7754757b6a85778ae050e75ef38401c9ec76
data/README.md CHANGED
@@ -59,7 +59,7 @@ Controlist.initialize YourManager #, attribute_proxy: "_val", value_object_proxy
59
59
  ## Example
60
60
 
61
61
  ```ruby
62
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
62
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
63
63
  Controlist::Permission.new(User, READ, true, [
64
64
  SimpleConstrain.new("name", "Tom"),
65
65
  SimpleConstrain.new("name", ["Grade 1", "Grade 2"], relation: "clazz"),
data/lib/controlist.rb CHANGED
@@ -8,10 +8,10 @@ module Controlist
8
8
 
9
9
  class << self
10
10
 
11
- attr_accessor :permission_provider, :attribute_proxy, :value_object_proxy, :logger
11
+ attr_accessor :permission_manager, :attribute_proxy, :value_object_proxy, :logger
12
12
 
13
- def initialize(permission_provider, config={})
14
- @permission_provider = permission_provider
13
+ def initialize(permission_manager, config={})
14
+ @permission_manager = permission_manager
15
15
  @attribute_proxy = config[:attribute_proxy] || "_val"
16
16
  @value_object_proxy = config[:value_object_proxy] || "_value_object"
17
17
  @logger = config[:logger] || Logger.new(STDOUT)
@@ -19,14 +19,23 @@ module Controlist
19
19
  end
20
20
 
21
21
  def skip
22
- @permission_provider.open_skip
23
- yield
24
- @permission_provider.close_skip
22
+ @permission_manager.enable_skip
23
+ result = yield
24
+ @permission_manager.disable_skip
25
+ result
25
26
  end
26
27
 
27
28
  def is_activerecord3?
28
29
  ActiveRecord::VERSION::MAJOR == 3
29
30
  end
31
+
32
+ def debug(*args, &block)
33
+ logger.debug *args, &block if @logger_enabled
34
+ end
35
+
36
+ def enable_logger
37
+ @logger_enabled = true
38
+ end
30
39
  end
31
40
 
32
41
  end
@@ -56,9 +56,9 @@ module Controlist
56
56
  Array(methods).each do |method|
57
57
  ActiveRecord::Persistence.module_eval %Q{
58
58
  def #{method}_with_controlist(*args)
59
- permission_provider = Controlist.permission_provider
60
- unless permission_provider.skip?
61
- permission_package = permission_provider.get_permission_package
59
+ permission_manager = Controlist.permission_manager
60
+ unless permission_manager.skip?
61
+ permission_package = permission_manager.get_permission_package
62
62
  permissions = permission_package.list_#{operation}[self.class] if permission_package
63
63
  if permissions.blank?
64
64
  raise NoPermissionError
@@ -67,7 +67,7 @@ module Controlist
67
67
  matched_permission = nil
68
68
  permissions.each do |permission|
69
69
  if permission.match_for_persistence(self, Controlist::Permission::#{operation.upcase})
70
- Controlist.logger.debug{"Controlist matched to \#{permission.is_allowed ? 'allow' : 'forbid'} \#{permission.inspect}"}
70
+ Controlist.debug{"Controlist matched to \#{permission.is_allowed ? 'allow' : 'forbid'} \#{permission.inspect}"}
71
71
  if permission.is_allowed
72
72
  passed = true
73
73
  end
@@ -76,9 +76,9 @@ module Controlist
76
76
  end
77
77
  end
78
78
  if passed
79
- Controlist.logger.debug{"Controlist #{operation} checked: PASSED"}
79
+ Controlist.debug{"Controlist #{operation} checked: PASSED"}
80
80
  else
81
- Controlist.logger.debug{"Controlist #{operation} checked: FORBIDDEN"}
81
+ Controlist.debug{"Controlist #{operation} checked: FORBIDDEN"}
82
82
  if matched_permission.nil?
83
83
  raise NoPermissionError
84
84
  else
@@ -98,18 +98,9 @@ module Controlist
98
98
  def hook_read
99
99
  if Controlist.is_activerecord3?
100
100
  ActiveRecord::QueryMethods.module_eval do
101
- def where!(opts, *rest)
102
- return if opts.blank?
103
- self.where_values += build_where(opts, rest)
104
- end
105
101
  def _select!(*value)
106
102
  self.select_values += Array.wrap(value)
107
103
  end
108
- def joins!(*args)
109
- return if args.compact.blank?
110
- args.flatten!
111
- self.joins_values += args
112
- end
113
104
  end
114
105
  ActiveRecord::IdentityMap.module_eval do
115
106
  def self.enabled?
@@ -131,31 +122,26 @@ module Controlist
131
122
  end
132
123
  end
133
124
  ActiveRecord::Relation.class_eval do
125
+ def real_build_arel_with_controlist
126
+ relation = self
127
+ permission_manager = Controlist.permission_manager
128
+ permission_package = permission_manager.get_permission_package
129
+ permissions = permission_package.list_read[@klass] if permission_package
130
+ if permissions.blank?
131
+ relation = self.where("1 != 1")
132
+ else
133
+ permissions.each do |permission|
134
+ relation = permission.handle_for_read relation
135
+ end
136
+ end
137
+ relation.send(:build_arel_without_controlist)
138
+ end
134
139
  def build_arel_with_controlist
135
- permission_provider = Controlist.permission_provider
136
- if permission_provider.skip? || @controlist_processing
140
+ permission_manager = Controlist.permission_manager
141
+ if permission_manager.skip?
137
142
  build_arel_without_controlist
138
143
  else
139
- if @controlist_done
140
- raise Controlist::NotReuseableError.new("The relation has built a sql, you can't reuse it, or you can clone it before sql building", self)
141
- else
142
- @controlist_processing = true
143
- permission_provider = Controlist.permission_provider
144
- unless permission_provider.skip?
145
- permission_package = permission_provider.get_permission_package
146
- permissions = permission_package.list_read[@klass] if permission_package
147
- if permissions.blank?
148
- self.where!("1 != 1")
149
- else
150
- permissions.each do |permission|
151
- permission.handle_for_read self
152
- end
153
- end
154
- end
155
- @controlist_processing = false
156
- @controlist_done = true
157
- build_arel_without_controlist
158
- end
144
+ self.real_build_arel_with_controlist
159
145
  end
160
146
  end
161
147
  alias_method_chain :build_arel, :controlist unless method_defined? :build_arel_without_controlist
@@ -16,11 +16,11 @@ module Controlist
16
16
  raise NotImplementedError
17
17
  end
18
18
 
19
- def open_skip
19
+ def enable_skip
20
20
  raise NotImplementedError
21
21
  end
22
22
 
23
- def close_skip
23
+ def disable_skip
24
24
  raise NotImplementedError
25
25
  end
26
26
 
@@ -16,11 +16,11 @@ module Controlist
16
16
  Thread.current[:skip_controlist] == true
17
17
  end
18
18
 
19
- def open_skip
19
+ def enable_skip
20
20
  Thread.current[:skip_controlist] = true
21
21
  end
22
22
 
23
- def close_skip
23
+ def disable_skip
24
24
  Thread.current[:skip_controlist] = nil
25
25
  end
26
26
 
@@ -10,7 +10,7 @@ module Controlist
10
10
  # properties is hash with property and value pair, operation READ only need keys
11
11
  attr_accessor :klass, :operations, :is_allowed, :constrains, :clause, :joins, :properties, :procs_read
12
12
 
13
- def initialize(klass, operations, is_allowed = true, constrains=nil)
13
+ def initialize(klass, operations=nil, is_allowed = true, constrains=nil)
14
14
  self.procs_read = []
15
15
  self.klass = klass
16
16
  unless operations.nil?
@@ -49,17 +49,14 @@ module Controlist
49
49
 
50
50
  def handle_for_read(relation)
51
51
  relation._select!(*self.properties.keys) unless self.properties.blank?
52
- relation.joins!(*self.joins) if self.joins.size > 0
53
- relation.where!("#{self.clause}") if self.clause
52
+ relation = relation.joins(*self.joins) if self.joins.size > 0
53
+ relation = relation.where("#{self.clause}") if self.clause
54
54
  unless self.procs_read.blank?
55
- # Only support ActiveRecord 4
56
- merging_relation = self.klass.unscoped
57
55
  self.procs_read.each do |proc|
58
- merging_relation = proc.call(merging_relation)
56
+ relation = proc.call(relation)
59
57
  end
60
- ActiveRecord::Relation::Merger.new(relation, merging_relation).merge
61
58
  end
62
-
59
+ relation
63
60
  end
64
61
 
65
62
  def match_for_persistence(object, operation)
@@ -78,7 +75,7 @@ module Controlist
78
75
  break
79
76
  end
80
77
  end
81
- Controlist.logger.debug{"Controlist #{operation} properties checked: #{properties_matched}"}
78
+ Controlist.debug{"Controlist #{operation} properties checked: #{properties_matched}"}
82
79
  properties_matched
83
80
  end
84
81
 
@@ -108,7 +105,7 @@ module Controlist
108
105
  inner_matched
109
106
  end
110
107
  end
111
- Controlist.logger.debug{"Controlist #{operation} constrains checked: #{constrain_matched}"}
108
+ Controlist.debug{"Controlist #{operation} constrains checked: #{constrain_matched}"}
112
109
  constrain_matched
113
110
  end
114
111
 
@@ -127,11 +124,15 @@ module Controlist
127
124
  self.constrains = constrains
128
125
  end
129
126
 
130
- def match_value(left, right, operator)
127
+ def match_value(target, value, operator)
131
128
  if operator.nil?
132
- left == right
129
+ if value.is_a? Array
130
+ value.include? target
131
+ else
132
+ target == value
133
+ end
133
134
  else
134
- left.send(operator.to_sym, right)
135
+ target.send(operator.to_sym, value)
135
136
  end
136
137
  end
137
138
 
@@ -10,12 +10,8 @@ module Controlist
10
10
  self.table_name = hash[:table_name]
11
11
  self.operator = hash[:operator]
12
12
  self.clause = hash[:clause]
13
- if Controlist.is_activerecord3? && (hash.has_key?(:proc_read) || hash.has_key?(:proc_persistence))
14
- raise NotImplementedError, "Skip proc_read and proc_persistence, that features only be supported in ActiveRecord 4 or later"
15
- else
16
- self.proc_read = hash[:proc_read]
17
- self.proc_persistence = hash[:proc_persistence]
18
- end
13
+ self.proc_read = hash[:proc_read]
14
+ self.proc_persistence = hash[:proc_persistence]
19
15
  end
20
16
 
21
17
  end
@@ -1,3 +1,3 @@
1
1
  module Controlist
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/test/feature_test.rb CHANGED
@@ -20,7 +20,7 @@ class FeatureTest < ActiveSupport::TestCase
20
20
  end
21
21
 
22
22
  def test_read_constrains
23
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
23
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
24
24
  Controlist::Permission.new(User, READ, true, [
25
25
  SimpleConstrain.new("name", "Tom"),
26
26
  AdvancedConstrain.new(property: "name", value: ["Grade 1", "Grade 2"], relation: "clazz"),
@@ -29,40 +29,34 @@ class FeatureTest < ActiveSupport::TestCase
29
29
  SimpleConstrain.new("age", [1,2,3]),
30
30
  SimpleConstrain.new("clazz_id", -> { Clazz.select(:id).map(&:id) }),
31
31
  AdvancedConstrain.new(clause: "age != 100"),
32
- Controlist.is_activerecord3? ? nil : AdvancedConstrain.new(proc_read: lambda{|relation| relation.order("id DESC").limit(3) })
32
+ AdvancedConstrain.new(proc_read: lambda{|relation| relation.order("id DESC").limit(3) })
33
33
  ])))
34
34
 
35
35
  relation = User.unscoped
36
- relation.to_sql
37
- assert_equal [:clazz], relation.joins_values
38
- assert_equal ["(users.name = 'Tom') and (clazzs.name in ('Grade 1','Grade 2'))" +
39
- " and (users.age >= 5) and (users.age is null) and (users.age in (1,2,3))" +
40
- " and (users.clazz_id in (1,2)) and (age != 100)"], relation.where_values
41
- unless Controlist.is_activerecord3?
42
- assert_equal 3, relation.limit_value
43
- assert_equal ["id DESC"], relation.order_values
44
- end
36
+ sql = relation.to_sql.gsub(/ +/, " ")
37
+ puts sql
38
+ assert_equal true, sql.include?("((users.name = 'Tom') and (clazzs.name in ('Grade 1','Grade 2')) and (users.age >= 5) and (users.age is null) and (users.age in (1,2,3)) and (users.clazz_id in (1,2,3)) and (age != 100)) ORDER BY id DESC LIMIT 3")
45
39
  end
46
40
 
47
41
  def test_permission_empty
48
- Controlist.permission_provider.set_permission_package(nil)
42
+ Controlist.permission_manager.set_permission_package(nil)
49
43
  relation = User.unscoped
50
- relation.to_sql
51
- assert_equal ["1 != 1"], relation.where_values
44
+ sql = relation.to_sql
45
+ assert_equal true, sql.include?("1 != 1")
52
46
  end
53
47
 
54
48
  def test_read_constrains_sql_only
55
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
49
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
56
50
  Controlist::Permission.new(User, READ, true, "age != 100")
57
51
  ))
58
52
  relation = User.unscoped
59
- relation.to_sql
60
- assert_equal ["age != 100"], relation.where_values
53
+ sql = relation.to_sql
54
+ assert_equal true, sql.include?("age != 100")
61
55
  end
62
56
 
63
57
 
64
58
  def test_read_apply_properties
65
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
59
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
66
60
  Controlist::Permission.new(User, READ).apply(:name)
67
61
  ))
68
62
 
@@ -70,7 +64,7 @@ class FeatureTest < ActiveSupport::TestCase
70
64
  assert_nil relation.first._value_object.clazz_id
71
65
  assert_nil relation.first._val(:clazz_id)
72
66
  assert_raise(ActiveModel::MissingAttributeError) { assert_nil relation.first.clazz_id }
73
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
67
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
74
68
  Controlist::Permission.new(User, READ)
75
69
  ))
76
70
 
@@ -81,7 +75,7 @@ class FeatureTest < ActiveSupport::TestCase
81
75
  end
82
76
 
83
77
  def test_update_fail_without_permissions
84
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
78
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
85
79
  Controlist::Permission.new(User, READ)
86
80
  ))
87
81
  user = User.find 1
@@ -92,33 +86,32 @@ class FeatureTest < ActiveSupport::TestCase
92
86
  end
93
87
 
94
88
  def test_persistence_constrains
95
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
89
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
96
90
  Controlist::Permission.new(Clazz, READ),
97
91
  Controlist::Permission.new(User, READ),
98
92
  Controlist::Permission.new(User, UPDATE, false, AdvancedConstrain.new(property: "name", value: "To", operator: "include?")),
99
- Controlist.is_activerecord3? ? nil : Controlist::Permission.new(User, UPDATE, false, AdvancedConstrain.new(proc_persistence: lambda{|object, operation| object.name == "Block"})),
100
- Controlist::Permission.new(User, [UPDATE, DELETE], false, AdvancedConstrain.new(property: "name", value: "Grade 1", relation: "clazz")),
93
+ Controlist::Permission.new(User, UPDATE, false, AdvancedConstrain.new(proc_persistence: lambda{|object, operation| object.name == "Block"})),
94
+ Controlist::Permission.new(User, [UPDATE, DELETE], false, AdvancedConstrain.new(property: "name", value: ["Grade 1", "Grade 3"], relation: "clazz")),
101
95
  Controlist::Permission.new(User, UPDATE)
102
96
  ))
103
97
 
104
98
  user = User.find 3
105
99
  assert_not_equal "Tom", user.name
106
100
  assert_not_equal "Grade 1", user.clazz.name
101
+ assert_not_equal "Grade 3", user.clazz.name
107
102
  user.name = "Test"
108
103
  assert_equal true, user.save
109
104
 
110
- unless Controlist.is_activerecord3?
111
- assert_raise(Controlist::PermissionForbidden) {
112
- user.name = "Block"
113
- user.save
114
- }
115
- end
105
+ assert_raise(Controlist::PermissionForbidden) {
106
+ user.name = "Block"
107
+ user.save
108
+ }
116
109
 
117
110
  assert_raise(Controlist::NoPermissionError) {
118
111
  user.destroy
119
112
  }
120
113
 
121
- Controlist.permission_provider.get_permission_package.add_permissions(Controlist::Permission.new(User, DELETE))
114
+ Controlist.permission_manager.get_permission_package.add_permissions(Controlist::Permission.new(User, DELETE))
122
115
  assert_instance_of User, user.destroy
123
116
 
124
117
  assert_raise(Controlist::PermissionForbidden) {
@@ -144,7 +137,7 @@ class FeatureTest < ActiveSupport::TestCase
144
137
  end
145
138
 
146
139
  def test_persistence_apply_properties
147
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
140
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
148
141
  Controlist::Permission.new(User, READ),
149
142
  Controlist::Permission.new(User, UPDATE, true, SimpleConstrain.new("name", "Tom")).apply(name: "Test", clazz_id: [1, 2]),
150
143
  ))
@@ -180,13 +173,13 @@ class FeatureTest < ActiveSupport::TestCase
180
173
  end
181
174
 
182
175
  def test_modify_permissions_on_the_fly
183
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
176
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
184
177
  Controlist::Permission.new(User, READ),
185
178
  ))
186
179
 
187
180
  assert_instance_of User, User.find(1)
188
181
 
189
- package = Controlist.permission_provider.get_permission_package
182
+ package = Controlist.permission_manager.get_permission_package
190
183
  package.remove_permissions package.permissions.last
191
184
 
192
185
  assert_raise(ActiveRecord::RecordNotFound) {
@@ -199,34 +192,20 @@ class FeatureTest < ActiveSupport::TestCase
199
192
 
200
193
  def test_constrains_argument_error
201
194
  assert_raise(ArgumentError) {
202
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
195
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
203
196
  Controlist::Permission.new(User, READ, true, Object.new)
204
197
  ))
205
198
  }
206
199
  end
207
200
 
208
- def test_relation_not_reuseable
209
- assert_raise(Controlist::NotReuseableError) {
210
- relation = User.unscoped
211
- relation.to_sql
212
- relation_new = relation.where("1 = 1")
213
- relation_new.to_sql
214
- }
215
- relation = User.unscoped
216
- reuseable_relation = relation.clone
217
- relation.to_sql
218
- relation_new = reuseable_relation.where("1 = 1")
219
- relation_new.to_sql
220
- end
221
-
222
201
  def test_skip
223
- Controlist.permission_provider.set_permission_package(OrderedPackage.new(
202
+ Controlist.permission_manager.set_permission_package(OrderedPackage.new(
224
203
  Controlist::Permission.new(User, READ, true, "age != 100")
225
204
  ))
226
205
  Controlist.skip do
227
206
  relation = User.unscoped
228
- relation.to_sql
229
- assert_equal [], relation.where_values
207
+ sql = relation.to_sql
208
+ assert_equal "SELECT \"users\".* FROM \"users\"", sql.strip
230
209
  end
231
210
  end
232
211
 
@@ -4,3 +4,6 @@ one:
4
4
  two:
5
5
  id: 2
6
6
  name: Grade 2
7
+ three:
8
+ id: 3
9
+ name: Grade 3
data/test/test.sqlite3 CHANGED
Binary file
data/test/test_helper.rb CHANGED
@@ -24,7 +24,7 @@ ActiveRecord::Base.logger = Logger.new(STDOUT)
24
24
 
25
25
  require 'controlist'
26
26
  require 'controlist/managers/thread_based_manager'
27
- #Controlist.initialize Controlist::Manager::ThreadBasedManager, attribute_proxy: "_val", value_object_proxy: "_value_object", logger: Logger.new(STDOUT)
27
+ #Controlist.initialize Controlist::Managers::ThreadBasedManager, attribute_proxy: "_val", value_object_proxy: "_value_object", logger: Logger.new(STDOUT)
28
28
  Controlist.initialize Controlist::Managers::ThreadBasedManager
29
29
 
30
30
  unless Controlist.is_activerecord3?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: controlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leon Li
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-03 00:00:00.000000000 Z
11
+ date: 2015-07-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -193,7 +193,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
193
193
  version: '0'
194
194
  requirements: []
195
195
  rubyforge_project:
196
- rubygems_version: 2.4.5
196
+ rubygems_version: 2.2.2
197
197
  signing_key:
198
198
  specification_version: 4
199
199
  summary: Fine-grained access control library for Ruby ActiveRecord
@@ -206,3 +206,4 @@ test_files:
206
206
  - test/models/user.rb
207
207
  - test/test.sqlite3
208
208
  - test/test_helper.rb
209
+ has_rdoc: