ae_declarative_authorization 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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