ae_declarative_authorization 0.7.0 → 0.7.1

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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Appraisals +21 -21
  3. data/CHANGELOG +189 -189
  4. data/Gemfile +7 -7
  5. data/Gemfile.lock +45 -45
  6. data/LICENSE.txt +20 -20
  7. data/README.md +620 -620
  8. data/README.rdoc +597 -597
  9. data/Rakefile +33 -33
  10. data/authorization_rules.dist.rb +20 -20
  11. data/declarative_authorization.gemspec +24 -24
  12. data/gemfiles/rails4252.gemfile +10 -10
  13. data/gemfiles/rails4271.gemfile +10 -10
  14. data/gemfiles/rails507.gemfile +11 -11
  15. data/init.rb +5 -5
  16. data/lib/declarative_authorization.rb +18 -18
  17. data/lib/declarative_authorization/authorization.rb +821 -821
  18. data/lib/declarative_authorization/helper.rb +78 -78
  19. data/lib/declarative_authorization/in_controller.rb +713 -713
  20. data/lib/declarative_authorization/in_model.rb +156 -156
  21. data/lib/declarative_authorization/maintenance.rb +215 -215
  22. data/lib/declarative_authorization/obligation_scope.rb +345 -345
  23. data/lib/declarative_authorization/railsengine.rb +5 -5
  24. data/lib/declarative_authorization/reader.rb +549 -549
  25. data/lib/declarative_authorization/test/helpers.rb +261 -261
  26. data/lib/declarative_authorization/version.rb +3 -3
  27. data/lib/generators/authorization/install/install_generator.rb +77 -77
  28. data/lib/generators/authorization/rules/rules_generator.rb +13 -13
  29. data/lib/generators/authorization/rules/templates/authorization_rules.rb +27 -27
  30. data/lib/tasks/authorization_tasks.rake +89 -89
  31. data/test/authorization_test.rb +1121 -1121
  32. data/test/controller_filter_resource_access_test.rb +573 -573
  33. data/test/controller_test.rb +478 -478
  34. data/test/database.yml +3 -3
  35. data/test/dsl_reader_test.rb +178 -178
  36. data/test/functional/filter_access_to_with_id_in_scope_test.rb +88 -88
  37. data/test/functional/no_filter_access_to_test.rb +79 -79
  38. data/test/functional/params_block_arity_test.rb +39 -39
  39. data/test/helper_test.rb +248 -248
  40. data/test/maintenance_test.rb +46 -46
  41. data/test/model_test.rb +1840 -1840
  42. data/test/schema.sql +60 -60
  43. data/test/test_helper.rb +174 -174
  44. data/test/test_support/minitest_compatibility.rb +26 -26
  45. metadata +3 -9
  46. data/gemfiles/rails4252.gemfile.lock +0 -126
  47. data/gemfiles/rails4271.gemfile.lock +0 -126
  48. data/gemfiles/rails507.gemfile.lock +0 -136
  49. data/log/test.log +0 -34715
  50. data/test/profiles/access_checking +0 -46
@@ -1,261 +1,261 @@
1
- require 'blockenspiel'
2
- require 'active_support/concern'
3
-
4
- module DeclarativeAuthorization
5
- module Test
6
- module Helpers
7
- extend ActiveSupport::Concern
8
-
9
- class InvalidParamsBlockArity < StandardError
10
- def initialize(params_block_name, params_block_arity, max_arity)
11
- message = "Params block '#{params_block_name}' has arity of #{params_block_arity}. Max params block arity is #{max_arity}."
12
- super(message)
13
- end
14
- end
15
-
16
- class PrivilegeTestGenerator
17
- include Blockenspiel::DSL
18
-
19
- def initialize(test_class, role, privileges)
20
- @test_class = test_class
21
- @role = role
22
- @privileges = [privileges].flatten
23
- end
24
-
25
- def allowed(options)
26
- role, privileges, actions, params_name = extract_options(options)
27
-
28
- actions.each do |action|
29
- privileges.each do |privilege|
30
- @test_class.send(:define_method, "test_#{action}__access_allowed__#{role}_role__#{privilege ? "#{privilege}_permissions__" : ""}with_#{params_name || 'no_params'}") do
31
- priv_param = (privilege == :hidden ? nil : privilege)
32
- if forbidden_with_role_and_privilege?(action, role, priv_param, params_name, options)
33
- flunk "The '#{action}' action #{params_name ? "with '#{params_name}' parameters " : ''}should be accessible for users with #{privilege ? "'#{privilege}' permissions for " : ""}the '#{role}' role."
34
- end
35
- end
36
- end
37
- end
38
- end
39
-
40
- def denied(options)
41
- role, privileges, actions, params_name = extract_options(options)
42
-
43
- actions.each do |action|
44
- privileges.each do |privilege|
45
- @test_class.send(:define_method, "test_#{action}__access_denied__#{role}_role__#{privilege ? "#{privilege}_permissions__" : ""}with_#{params_name || 'no_params'}") do
46
- priv_param = (privilege == :hidden ? nil : privilege)
47
- unless forbidden_with_role_and_privilege?(action, role, priv_param, params_name, options)
48
- flunk "The '#{action}' action #{params_name ? "with '#{params_name}' parameters " : ''}should NOT be accessible for users with #{privilege ? "'#{privilege}' permissions for " : ""}the '#{role}' role."
49
- end
50
- end
51
- end
52
- end
53
- end
54
-
55
- protected
56
-
57
- def extract_options(options)
58
- # Can't use these instance variable from inside a method on the test class
59
- role = @role
60
- privileges = @privileges
61
-
62
- actions = options[:to]
63
- raise ':to is a required option!' unless actions
64
-
65
- actions = [actions] unless actions.is_a?(Array)
66
- params_name = options[:with]
67
-
68
- [role, privileges, actions, params_name]
69
- end
70
-
71
- end
72
-
73
- class RoleTestGenerator
74
- include Blockenspiel::DSL
75
-
76
- def initialize(test_class, role)
77
- @test_class = test_class
78
- @role = role
79
- end
80
-
81
- def privilege(privilege, &block)
82
- privileges = [privilege].flatten.uniq
83
-
84
- unless privileges.all? { |privilege| [:hidden, :read, :write].include?(privilege) }
85
- raise "Privilege (:when) must be :hidden, :read, or :write. Found #{privilege.inspect}."
86
- end
87
-
88
- Blockenspiel.invoke(block, PrivilegeTestGenerator.new(@test_class, @role, privileges))
89
- end
90
-
91
- def allowed(options)
92
- if options[:when]
93
- privilege(options[:when]) { allowed(options) }
94
- else
95
- Blockenspiel.invoke(Proc.new {allowed(options)}, PrivilegeTestGenerator.new(@test_class, @role, nil))
96
- end
97
- end
98
-
99
- def denied(options)
100
- if options[:when]
101
- privilege(options[:when]) { denied(options) }
102
- else
103
- Blockenspiel.invoke(Proc.new {denied(options)}, PrivilegeTestGenerator.new(@test_class, @role, nil))
104
- end
105
- end
106
-
107
- end
108
-
109
- class AccessTestGenerator
110
- include Blockenspiel::DSL
111
-
112
- def initialize(test_class)
113
- @test_class = test_class
114
- end
115
-
116
- def params(name, &block)
117
- @test_class.define_access_test_params_method(name, &block)
118
- end
119
-
120
- def role(role, &block)
121
- raise "Role cannot be blank!" if role.blank?
122
- Blockenspiel.invoke(block, RoleTestGenerator.new(@test_class, role))
123
- end
124
-
125
- end
126
-
127
- module ClassMethods
128
- attr_reader :access_tests_defined
129
-
130
- def skip_access_tests_for_actions(*actions)
131
- @skipped_access_test_actions ||= []
132
- @skipped_access_test_actions += actions.map(&:to_sym)
133
- end
134
-
135
- def access_tests(&block)
136
- @access_tests_defined = true
137
- file_output ||= [ 'test/profiles/access_checking', ENV['TEST_ENV_NUMBER'] ].compact.join('.')
138
- unless File.exists?(file_output)
139
- FileUtils.mkdir_p(File.dirname(file_output))
140
- end
141
- File.open(file_output, "a+") do |file|
142
- file.puts self.controller_class.name
143
- end
144
-
145
- Blockenspiel.invoke(block, AccessTestGenerator.new(self))
146
- end
147
-
148
- def this_is_an_abstract_controller_so_it_needs_no_access_tests
149
- undef_method :test_access_tests_defined if self.method_defined? :test_access_tests_defined
150
- undef_method :test_all_public_actions_covered_by_role_tests if self.method_defined? :test_all_public_actions_covered_by_role_tests
151
- end
152
-
153
- alias :this_is_a_module_mixed_into_controllers_so_it_needs_no_access_tests :this_is_an_abstract_controller_so_it_needs_no_access_tests
154
- alias :the_access_tests_are_tested_elsewhere_so_no_access_tests_are_needed :this_is_an_abstract_controller_so_it_needs_no_access_tests
155
- alias :access_tests_not_required :this_is_an_abstract_controller_so_it_needs_no_access_tests
156
-
157
- def all_public_actions
158
- actions = controller_class.public_instance_methods(false)
159
- actions += controller_class.superclass.public_instance_methods(false)
160
- actions.reject! do |method|
161
- method =~ /^_/ ||
162
- method =~ /^rescue_action/ ||
163
- (@skipped_access_test_actions.is_a?(Array) && @skipped_access_test_actions.include?(method))
164
- end
165
-
166
- actions.uniq
167
- end
168
-
169
- def inherited(child)
170
- super
171
-
172
- child.send(:define_method, :test_access_tests_defined) do
173
- assert self.class.access_tests_defined, 'Access tests needed but not defined.'
174
- end
175
-
176
- child.send(:define_method, :test_all_public_actions_covered_by_role_tests) do
177
- test_methods = self.public_methods(false).select { |method| method =~ /^test_/ }
178
- untested_actions = self.class.all_public_actions.select { |action| !test_methods.any? { |method| method =~ /^test_#{action}__access_/} }
179
- unless untested_actions.empty?
180
- flunk "In #{self.class.name}, it appears that #{untested_actions.map(&:inspect).to_sentence} are not tested by any access_tests. Did you forget them?"
181
- end
182
- end
183
- end
184
-
185
- def define_access_test_params_method(name, &block)
186
- define_method("access_test_params_for_#{name}", &block)
187
- end
188
-
189
- end
190
-
191
- protected
192
-
193
- def response_forbidden?
194
- flash[:error] == 'You do not have the correct permissions to access that page. Click the back button to return to your previous page.' ||
195
- flash[:error] =~ /You do not have the correct permissions to view this/ ||
196
- flash[:error] =~ /You do not have access to/ ||
197
- flash[:alert] =~ /You need to sign in/ ||
198
- (@response.location =~ /\/users\/sign_in/ && @response.code == "302")
199
- end
200
-
201
- def access_test_params_for_param_methods
202
- []
203
- end
204
-
205
- def access_test_params(name)
206
- return { } unless name.present?
207
-
208
- params = access_test_params_for_param_methods
209
- max_arity = params.size
210
-
211
- full_method_name = "access_test_params_for_#{name}"
212
- method_arity = method(full_method_name).arity
213
-
214
- unless method_arity <= max_arity
215
- raise InvalidParamsBlockArity.new(name, method_arity, max_arity)
216
- end
217
-
218
- send(full_method_name, *params[0...method_arity])
219
- end
220
-
221
- def access_test_user(role, privilege)
222
- raise 'MUST IMPLEMENT!!!'
223
- end
224
-
225
- def forbidden_with_role_and_privilege?(action, role, privilege, params_name = nil, options = {})
226
- http_method = options[:method] || :get
227
- xhr = options[:xhr]
228
-
229
- user = access_test_user(role, privilege)
230
- params = access_test_params(params_name)
231
-
232
- send_args = [http_method, action.to_sym]
233
- send_kwargs = { params: params }
234
- send_kwargs[:xhr] = true if xhr
235
-
236
- errors_to_reraise = [
237
- ActionController::RoutingError,
238
- ActionController::UrlGenerationError,
239
- AbstractController::ActionNotFound
240
- ]
241
-
242
- errors_to_reraise << Mocha::ExpectationError if defined?(Mocha::ExpectationError)
243
-
244
- begin
245
- send *send_args, **send_kwargs
246
- return response_forbidden?
247
- rescue *errors_to_reraise => e
248
- raise e
249
- rescue => e
250
- if options[:print_error]
251
- puts "\n#{e.class.name} raised in action '#{action}':"
252
- puts e.message
253
- puts e.backtrace.join("\n")
254
- end
255
- return false
256
- end
257
- end
258
-
259
- end
260
- end
261
- end
1
+ require 'blockenspiel'
2
+ require 'active_support/concern'
3
+
4
+ module DeclarativeAuthorization
5
+ module Test
6
+ module Helpers
7
+ extend ActiveSupport::Concern
8
+
9
+ class InvalidParamsBlockArity < StandardError
10
+ def initialize(params_block_name, params_block_arity, max_arity)
11
+ message = "Params block '#{params_block_name}' has arity of #{params_block_arity}. Max params block arity is #{max_arity}."
12
+ super(message)
13
+ end
14
+ end
15
+
16
+ class PrivilegeTestGenerator
17
+ include Blockenspiel::DSL
18
+
19
+ def initialize(test_class, role, privileges)
20
+ @test_class = test_class
21
+ @role = role
22
+ @privileges = [privileges].flatten
23
+ end
24
+
25
+ def allowed(options)
26
+ role, privileges, actions, params_name = extract_options(options)
27
+
28
+ actions.each do |action|
29
+ privileges.each do |privilege|
30
+ @test_class.send(:define_method, "test_#{action}__access_allowed__#{role}_role__#{privilege ? "#{privilege}_permissions__" : ""}with_#{params_name || 'no_params'}") do
31
+ priv_param = (privilege == :hidden ? nil : privilege)
32
+ if forbidden_with_role_and_privilege?(action, role, priv_param, params_name, options)
33
+ flunk "The '#{action}' action #{params_name ? "with '#{params_name}' parameters " : ''}should be accessible for users with #{privilege ? "'#{privilege}' permissions for " : ""}the '#{role}' role."
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def denied(options)
41
+ role, privileges, actions, params_name = extract_options(options)
42
+
43
+ actions.each do |action|
44
+ privileges.each do |privilege|
45
+ @test_class.send(:define_method, "test_#{action}__access_denied__#{role}_role__#{privilege ? "#{privilege}_permissions__" : ""}with_#{params_name || 'no_params'}") do
46
+ priv_param = (privilege == :hidden ? nil : privilege)
47
+ unless forbidden_with_role_and_privilege?(action, role, priv_param, params_name, options)
48
+ flunk "The '#{action}' action #{params_name ? "with '#{params_name}' parameters " : ''}should NOT be accessible for users with #{privilege ? "'#{privilege}' permissions for " : ""}the '#{role}' role."
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ protected
56
+
57
+ def extract_options(options)
58
+ # Can't use these instance variable from inside a method on the test class
59
+ role = @role
60
+ privileges = @privileges
61
+
62
+ actions = options[:to]
63
+ raise ':to is a required option!' unless actions
64
+
65
+ actions = [actions] unless actions.is_a?(Array)
66
+ params_name = options[:with]
67
+
68
+ [role, privileges, actions, params_name]
69
+ end
70
+
71
+ end
72
+
73
+ class RoleTestGenerator
74
+ include Blockenspiel::DSL
75
+
76
+ def initialize(test_class, role)
77
+ @test_class = test_class
78
+ @role = role
79
+ end
80
+
81
+ def privilege(privilege, &block)
82
+ privileges = [privilege].flatten.uniq
83
+
84
+ unless privileges.all? { |privilege| [:hidden, :read, :write, :write_without_delete].include?(privilege) }
85
+ raise "Privilege (:when) must be :hidden, :read, :write_without_delete, or :write. Found #{privilege.inspect}."
86
+ end
87
+
88
+ Blockenspiel.invoke(block, PrivilegeTestGenerator.new(@test_class, @role, privileges))
89
+ end
90
+
91
+ def allowed(options)
92
+ if options[:when]
93
+ privilege(options[:when]) { allowed(options) }
94
+ else
95
+ Blockenspiel.invoke(Proc.new {allowed(options)}, PrivilegeTestGenerator.new(@test_class, @role, nil))
96
+ end
97
+ end
98
+
99
+ def denied(options)
100
+ if options[:when]
101
+ privilege(options[:when]) { denied(options) }
102
+ else
103
+ Blockenspiel.invoke(Proc.new {denied(options)}, PrivilegeTestGenerator.new(@test_class, @role, nil))
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+ class AccessTestGenerator
110
+ include Blockenspiel::DSL
111
+
112
+ def initialize(test_class)
113
+ @test_class = test_class
114
+ end
115
+
116
+ def params(name, &block)
117
+ @test_class.define_access_test_params_method(name, &block)
118
+ end
119
+
120
+ def role(role, &block)
121
+ raise "Role cannot be blank!" if role.blank?
122
+ Blockenspiel.invoke(block, RoleTestGenerator.new(@test_class, role))
123
+ end
124
+
125
+ end
126
+
127
+ module ClassMethods
128
+ attr_reader :access_tests_defined
129
+
130
+ def skip_access_tests_for_actions(*actions)
131
+ @skipped_access_test_actions ||= []
132
+ @skipped_access_test_actions += actions.map(&:to_sym)
133
+ end
134
+
135
+ def access_tests(&block)
136
+ @access_tests_defined = true
137
+ file_output ||= [ 'test/profiles/access_checking', ENV['TEST_ENV_NUMBER'] ].compact.join('.')
138
+ unless File.exists?(file_output)
139
+ FileUtils.mkdir_p(File.dirname(file_output))
140
+ end
141
+ File.open(file_output, "a+") do |file|
142
+ file.puts self.controller_class.name
143
+ end
144
+
145
+ Blockenspiel.invoke(block, AccessTestGenerator.new(self))
146
+ end
147
+
148
+ def this_is_an_abstract_controller_so_it_needs_no_access_tests
149
+ undef_method :test_access_tests_defined if self.method_defined? :test_access_tests_defined
150
+ undef_method :test_all_public_actions_covered_by_role_tests if self.method_defined? :test_all_public_actions_covered_by_role_tests
151
+ end
152
+
153
+ alias :this_is_a_module_mixed_into_controllers_so_it_needs_no_access_tests :this_is_an_abstract_controller_so_it_needs_no_access_tests
154
+ alias :the_access_tests_are_tested_elsewhere_so_no_access_tests_are_needed :this_is_an_abstract_controller_so_it_needs_no_access_tests
155
+ alias :access_tests_not_required :this_is_an_abstract_controller_so_it_needs_no_access_tests
156
+
157
+ def all_public_actions
158
+ actions = controller_class.public_instance_methods(false)
159
+ actions += controller_class.superclass.public_instance_methods(false)
160
+ actions.reject! do |method|
161
+ method =~ /^_/ ||
162
+ method =~ /^rescue_action/ ||
163
+ (@skipped_access_test_actions.is_a?(Array) && @skipped_access_test_actions.include?(method))
164
+ end
165
+
166
+ actions.uniq
167
+ end
168
+
169
+ def inherited(child)
170
+ super
171
+
172
+ child.send(:define_method, :test_access_tests_defined) do
173
+ assert self.class.access_tests_defined, 'Access tests needed but not defined.'
174
+ end
175
+
176
+ child.send(:define_method, :test_all_public_actions_covered_by_role_tests) do
177
+ test_methods = self.public_methods(false).select { |method| method =~ /^test_/ }
178
+ untested_actions = self.class.all_public_actions.select { |action| !test_methods.any? { |method| method =~ /^test_#{action}__access_/} }
179
+ unless untested_actions.empty?
180
+ flunk "In #{self.class.name}, it appears that #{untested_actions.map(&:inspect).to_sentence} are not tested by any access_tests. Did you forget them?"
181
+ end
182
+ end
183
+ end
184
+
185
+ def define_access_test_params_method(name, &block)
186
+ define_method("access_test_params_for_#{name}", &block)
187
+ end
188
+
189
+ end
190
+
191
+ protected
192
+
193
+ def response_forbidden?
194
+ flash[:error] == 'You do not have the correct permissions to access that page. Click the back button to return to your previous page.' ||
195
+ flash[:error] =~ /You do not have the correct permissions to view this/ ||
196
+ flash[:error] =~ /You do not have access to/ ||
197
+ flash[:alert] =~ /You need to sign in/ ||
198
+ (@response.location =~ /\/users\/sign_in/ && @response.code == "302")
199
+ end
200
+
201
+ def access_test_params_for_param_methods
202
+ []
203
+ end
204
+
205
+ def access_test_params(name)
206
+ return { } unless name.present?
207
+
208
+ params = access_test_params_for_param_methods
209
+ max_arity = params.size
210
+
211
+ full_method_name = "access_test_params_for_#{name}"
212
+ method_arity = method(full_method_name).arity
213
+
214
+ unless method_arity <= max_arity
215
+ raise InvalidParamsBlockArity.new(name, method_arity, max_arity)
216
+ end
217
+
218
+ send(full_method_name, *params[0...method_arity])
219
+ end
220
+
221
+ def access_test_user(role, privilege)
222
+ raise 'MUST IMPLEMENT!!!'
223
+ end
224
+
225
+ def forbidden_with_role_and_privilege?(action, role, privilege, params_name = nil, options = {})
226
+ http_method = options[:method] || :get
227
+ xhr = options[:xhr]
228
+
229
+ user = access_test_user(role, privilege)
230
+ params = access_test_params(params_name)
231
+
232
+ send_args = [http_method, action.to_sym]
233
+ send_kwargs = { params: params }
234
+ send_kwargs[:xhr] = true if xhr
235
+
236
+ errors_to_reraise = [
237
+ ActionController::RoutingError,
238
+ ActionController::UrlGenerationError,
239
+ AbstractController::ActionNotFound
240
+ ]
241
+
242
+ errors_to_reraise << Mocha::ExpectationError if defined?(Mocha::ExpectationError)
243
+
244
+ begin
245
+ send *send_args, **send_kwargs
246
+ return response_forbidden?
247
+ rescue *errors_to_reraise => e
248
+ raise e
249
+ rescue => e
250
+ if options[:print_error]
251
+ puts "\n#{e.class.name} raised in action '#{action}':"
252
+ puts e.message
253
+ puts e.backtrace.join("\n")
254
+ end
255
+ return false
256
+ end
257
+ end
258
+
259
+ end
260
+ end
261
+ end