ae_declarative_authorization 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,5 +7,6 @@ gem "mocha", "~> 1.0", require: false
7
7
  gem "sqlite3"
8
8
  gem "rails", "5.1.6"
9
9
  gem "rails-controller-testing"
10
+ gem 'grape', "~> 1.1"
10
11
 
11
12
  gemspec path: "../"
@@ -7,5 +7,6 @@ gem "mocha", "~> 1.0", require: false
7
7
  gem "sqlite3"
8
8
  gem "rails", "5.2.1"
9
9
  gem "rails-controller-testing"
10
+ gem 'grape', "~> 1.1"
10
11
 
11
12
  gemspec path: "../"
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- ae_declarative_authorization (0.7.1)
4
+ ae_declarative_authorization (0.8.0)
5
5
  blockenspiel (~> 0.5.0)
6
6
  rails (>= 4.2.5.2, < 6)
7
7
 
@@ -68,7 +68,7 @@ GEM
68
68
  nokogiri (>= 1.5.9)
69
69
  mail (2.7.0)
70
70
  mini_mime (>= 0.1.1)
71
- marcel (0.3.2)
71
+ marcel (0.3.3)
72
72
  mimemagic (~> 0.3.2)
73
73
  metaclass (0.0.4)
74
74
  method_source (0.9.0)
@@ -1,18 +1,23 @@
1
1
  require File.join(%w{declarative_authorization helper})
2
- require File.join(%w{declarative_authorization in_controller})
2
+ if defined?(ActionController)
3
+ require File.dirname(__FILE__) + '/declarative_authorization/controller/rails.rb'
4
+ end
5
+
3
6
  if defined?(ActiveRecord)
4
7
  require File.join(%w{declarative_authorization in_model})
5
8
  require File.join(%w{declarative_authorization obligation_scope})
6
9
  end
7
10
 
8
- min_rails_version = '4.2.5.2'
9
- if Rails::VERSION::STRING < min_rails_version
11
+ min_rails_version = Gem::Version.new('4.2.5.2')
12
+ if Gem::Version.new(Rails::VERSION::STRING) < min_rails_version
10
13
  raise "ae_declarative_authorization requires Rails #{min_rails_version}. You are using #{Rails::VERSION::STRING}."
11
14
  end
12
15
 
13
16
  require File.join(%w{declarative_authorization railsengine}) if defined?(::Rails::Engine)
14
17
 
15
- ActionController::Base.send :include, Authorization::AuthorizationInController
16
- ActionController::Base.helper Authorization::AuthorizationHelper
18
+ if defined?(ActionController)
19
+ ActionController::Base.send :include, Authorization::Controller::Rails
20
+ ActionController::Base.helper Authorization::AuthorizationHelper
21
+ end
17
22
 
18
23
  ActiveRecord::Base.send :include, Authorization::AuthorizationInModel if defined?(ActiveRecord)
@@ -0,0 +1,208 @@
1
+ require File.dirname(__FILE__) + '/../controller_permission.rb'
2
+
3
+ module Authorization
4
+ module Controller
5
+ module DSL
6
+ #
7
+ # Defines a filter to be applied according to the authorization of the
8
+ # current user. Requires at least one symbol corresponding to an
9
+ # action as parameter. The special symbol :+all+ refers to all actions.
10
+ # The all :+all+ statement is only employed if no specific statement is
11
+ # present.
12
+ # class UserController < ApplicationController
13
+ # filter_access_to :index
14
+ # filter_access_to :new, :edit
15
+ # filter_access_to :all
16
+ # ...
17
+ # end
18
+ #
19
+ # The default is to allow access unconditionally if no rule matches.
20
+ # Thus, including the +filter_access_to+ :+all+ statement is a good
21
+ # idea, implementing a default-deny policy.
22
+ #
23
+ # When the access is denied, the method +permission_denied+ is called
24
+ # on the current controller, if defined. Else, a simple "you are not
25
+ # allowed" string is output. Log.info is given more information on the
26
+ # reasons of denial.
27
+ #
28
+ # def permission_denied
29
+ # flash[:error] = 'Sorry, you are not allowed to the requested page.'
30
+ # respond_to do |format|
31
+ # format.html { redirect_to(:back) rescue redirect_to('/') }
32
+ # format.xml { head :unauthorized }
33
+ # format.js { head :unauthorized }
34
+ # end
35
+ # end
36
+ #
37
+ # By default, required privileges are inferred from the action name and
38
+ # the controller name. Thus, in UserController :+edit+ requires
39
+ # :+edit+ +users+. To specify required privilege, use the option :+require+
40
+ # filter_access_to :new, :create, :require => :create, :context => :users
41
+ #
42
+ # Without the :+attribute_check+ option, no constraints from the
43
+ # authorization rules are enforced because for some actions (collections,
44
+ # +new+, +create+), there is no object to evaluate conditions against. To
45
+ # allow attribute checks on all actions, it is a common pattern to provide
46
+ # custom objects through +before_actions+:
47
+ # class BranchesController < ApplicationController
48
+ # before_action :load_company
49
+ # before_action :new_branch_from_company_and_params,
50
+ # :only => [:index, :new, :create]
51
+ # filter_access_to :all, :attribute_check => true
52
+ #
53
+ # protected
54
+ # def new_branch_from_company_and_params
55
+ # @branch = @company.branches.new(params[:branch])
56
+ # end
57
+ # end
58
+ # NOTE: +before_actions+ need to be defined before the first
59
+ # +filter_access_to+ call.
60
+ #
61
+ # For further customization, a custom filter expression may be formulated
62
+ # in a block, which is then evaluated in the context of the controller
63
+ # on a matching request. That is, for checking two objects, use the
64
+ # following:
65
+ # filter_access_to :merge do
66
+ # permitted_to!(:update, User.find(params[:original_id])) and
67
+ # permitted_to!(:delete, User.find(params[:id]))
68
+ # end
69
+ # The block should raise a Authorization::AuthorizationError or return
70
+ # false if the access is to be denied.
71
+ #
72
+ # Later calls to filter_access_to with overlapping actions overwrite
73
+ # previous ones for that action.
74
+ #
75
+ # All options:
76
+ # [:+require+]
77
+ # Privilege required; defaults to action_name
78
+ # [:+context+]
79
+ # The privilege's context, defaults to decl_auth_context, which consists
80
+ # of controller_name, prepended by any namespaces
81
+ # [:+attribute_check+]
82
+ # Enables the check of attributes defined in the authorization rules.
83
+ # Defaults to false. If enabled, filter_access_to will use a context
84
+ # object from one of the following sources (in that order):
85
+ # * the method from the :+load_method+ option,
86
+ # * an instance variable named after the singular of the context
87
+ # (by default from the controller name, e.g. @post for PostsController),
88
+ # * a find on the context model, using +params+[:id] as id value.
89
+ # Any of these methods will only be employed if :+attribute_check+
90
+ # is enabled.
91
+ # [:+model+]
92
+ # The data model to load a context object from. Defaults to the
93
+ # context, singularized.
94
+ # [:+load_method+]
95
+ # Specify a method by symbol or a Proc object which should be used
96
+ # to load the object. Both should return the loaded object.
97
+ # If a Proc object is given, e.g. by way of
98
+ # +lambda+, it is called in the instance of the controller.
99
+ # Example demonstrating the default behavior:
100
+ # filter_access_to :show, :attribute_check => true,
101
+ # :load_method => lambda { User.find(params[:id]) }
102
+ #
103
+
104
+ def filter_access_to(*args, &filter_block)
105
+ options = args.last.is_a?(Hash) ? args.pop : {}
106
+ options = {
107
+ :require => nil,
108
+ :context => nil,
109
+ :attribute_check => false,
110
+ :model => nil,
111
+ :load_method => nil,
112
+ :strong_parameters => nil
113
+ }.merge!(options)
114
+ privilege = options[:require]
115
+ context = options[:context]
116
+ actions = args.flatten
117
+
118
+ reset_filter!
119
+
120
+ filter_access_permissions.each do |perm|
121
+ perm.remove_actions(actions)
122
+ end
123
+ filter_access_permissions <<
124
+ ControllerPermission.new(actions, privilege, context,
125
+ options[:strong_parameters],
126
+ options[:attribute_check],
127
+ options[:model],
128
+ options[:load_method],
129
+ filter_block)
130
+ end
131
+
132
+ # Disables authorization entirely. Requires at least one symbol corresponding
133
+ # to an action as parameter. The special symbol :+all+ refers to all actions.
134
+ # The all :+all+ statement is only employed if no specific statement is
135
+ # present.
136
+ def no_filter_access_to(*args)
137
+ filter_access_to args do
138
+ true
139
+ end
140
+ end
141
+
142
+ # Collecting all the ControllerPermission objects from the controller
143
+ # hierarchy. Permissions for actions are overwritten by calls to
144
+ # filter_access_to in child controllers with the same action.
145
+ def all_filter_access_permissions # :nodoc:
146
+ ancestors.inject([]) do |perms, mod|
147
+ if mod.respond_to?(:filter_access_permissions, true)
148
+ perms +
149
+ mod.filter_access_permissions.collect do |p1|
150
+ p1.clone.remove_actions(perms.inject(Set.new) {|actions, p2| actions + p2.actions})
151
+ end
152
+ else
153
+ perms
154
+ end
155
+ end
156
+ end
157
+
158
+ # Returns the context for authorization checks in the current controller.
159
+ # Uses the controller_name and prepends any namespaces underscored and
160
+ # joined with underscores.
161
+ #
162
+ # E.g.
163
+ # AllThosePeopleController => :all_those_people
164
+ # AnyName::Space::ThingsController => :any_name_space_things
165
+ #
166
+ def decl_auth_context
167
+ prefixes = name.split('::')[0..-2].map(&:underscore)
168
+ ((prefixes + [controller_name]) * '_').to_sym
169
+ end
170
+
171
+ protected
172
+
173
+ def filter_access_permissions # :nodoc:
174
+ unless filter_access_permissions?
175
+ ancestors[1..-1].reverse.each do |mod|
176
+ mod.filter_access_permissions if mod.respond_to?(:filter_access_permissions, true)
177
+ end
178
+ end
179
+ class_variable_set(:@@declarative_authorization_permissions, {}) unless filter_access_permissions?
180
+ class_variable_get(:@@declarative_authorization_permissions)[self.name] ||= []
181
+ end
182
+
183
+ def filter_access_permissions? # :nodoc:
184
+ class_variable_defined?(:@@declarative_authorization_permissions)
185
+ end
186
+
187
+ def actions_from_option(option) # :nodoc:
188
+ case option
189
+ when nil
190
+ {}
191
+ when Symbol, String
192
+ {option.to_sym => option.to_sym}
193
+ when Hash
194
+ option
195
+ when Enumerable
196
+ option.each_with_object({}) do |action, hash|
197
+ if action.is_a?(Array)
198
+ raise "Unexpected option format: #{option.inspect}" if action.length != 2
199
+ hash[action.first] = action.last
200
+ else
201
+ hash[action.to_sym] = action.to_sym
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,79 @@
1
+ require File.dirname(__FILE__) + '/../authorization.rb'
2
+ require File.dirname(__FILE__) + '/dsl.rb'
3
+ require File.dirname(__FILE__) + '/runtime.rb'
4
+
5
+ #
6
+ # This mixin can be used to add declarative authorization support to APIs built using Grape
7
+ # https://github.com/ruby-grape/grape
8
+ #
9
+ # Usage:
10
+ # class MyApi < Grape::API
11
+ # include Authorization::Controller::Grape
12
+ #
13
+ # get :hello do
14
+ # end
15
+ # end
16
+ #
17
+ # NOTE: actions in authorization rules must be named `{METHOD} {URL}`. eg
18
+ # has_permission_on :my_api, to: 'GET /my_api/hello'
19
+ #
20
+ module Authorization
21
+ module Controller
22
+ module Grape
23
+ def self.included(base) # :nodoc:
24
+ base.extend ClassMethods
25
+
26
+ base.extend ::Authorization::Controller::DSL
27
+
28
+ base.module_eval do
29
+ add_filter!
30
+ end
31
+
32
+ base.helpers do
33
+ include ::Authorization::Controller::Runtime
34
+
35
+ def authorization_engine
36
+ ::Authorization::Engine.instance
37
+ end
38
+
39
+ def filter_access_filter # :nodoc:
40
+ unless allowed?("#{request.request_method} #{route.origin}")
41
+ if respond_to?(:permission_denied, true)
42
+ # permission_denied needs to render or redirect
43
+ send(:permission_denied)
44
+ else
45
+ error!('You are not allowed to access this action.', 403)
46
+ end
47
+ end
48
+ end
49
+
50
+ def logger
51
+ ::Rails.logger
52
+ end
53
+
54
+ protected
55
+
56
+ def api_class
57
+ options[:for]
58
+ end
59
+ end
60
+ end
61
+
62
+ module ClassMethods
63
+ def controller_name
64
+ name.demodulize.underscore
65
+ end
66
+
67
+ def add_filter!
68
+ before do
69
+ send(:filter_access_filter)
70
+ end
71
+ end
72
+
73
+ def reset_filter!
74
+ # Not required with Grape
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,340 @@
1
+ require File.dirname(__FILE__) + '/../authorization.rb'
2
+ require File.dirname(__FILE__) + '/dsl.rb'
3
+ require File.dirname(__FILE__) + '/runtime.rb'
4
+
5
+ #
6
+ # Mixin to be added to rails controllers
7
+ #
8
+ module Authorization
9
+ module Controller
10
+ module Rails
11
+ def self.included(base) # :nodoc:
12
+ base.extend ClassMethods
13
+
14
+ base.extend DSL
15
+
16
+ base.module_eval do
17
+ add_filter!
18
+ end
19
+
20
+ base.include Runtime
21
+ end
22
+
23
+ module ClassMethods
24
+ #
25
+ # Add the filtering before_action
26
+ #
27
+ def add_filter!
28
+ before_action(:filter_access_filter)
29
+ end
30
+
31
+ #
32
+ # Move the filtering to the end of the before_action list
33
+ #
34
+ def reset_filter!
35
+ skip_before_action(:filter_access_filter) if method_defined?(:filter_access_filter)
36
+ before_action :filter_access_filter
37
+ end
38
+
39
+ # To DRY up the filter_access_to statements in restful controllers,
40
+ # filter_resource_access combines typical filter_access_to and
41
+ # before_action calls, which set up the instance variables.
42
+ #
43
+ # The simplest case are top-level resource controllers with only the
44
+ # seven CRUD methods, e.g.
45
+ # class CompanyController < ApplicationController
46
+ # filter_resource_access
47
+ #
48
+ # def index...
49
+ # end
50
+ # Here, all CRUD actions are protected through a filter_access_to :all
51
+ # statement. :+attribute_check+ is enabled for all actions except for
52
+ # the collection action :+index+. To have an object for attribute checks
53
+ # available, filter_resource_access will set the instance variable
54
+ # @+company+ in before filters. For the member actions (:+show+, :+edit+,
55
+ # :+update+, :+destroy+) @company is set to Company.find(params[:id]).
56
+ # For +new+ actions (:+new+, :+create+), filter_resource_access creates
57
+ # a new object from company parameters: Company.new(params[:company].
58
+ #
59
+ # For nested resources, the parent object may be loaded automatically.
60
+ # class BranchController < ApplicationController
61
+ # filter_resource_access :nested_in => :companies
62
+ # end
63
+ # Again, the CRUD actions are protected. Now, for all CRUD actions,
64
+ # the parent object @company is loaded from params[:company_id]. It is
65
+ # also used when creating @branch for +new+ actions. Here, attribute_check
66
+ # is enabled for the collection :+index+ as well, checking attributes on a
67
+ # @company.branches.new method.
68
+ #
69
+ # In many cases, the default seven CRUD actions are not sufficient. As in
70
+ # the resource definition for routing you may thus give additional member,
71
+ # new and collection methods. The +options+ allow you to specify the
72
+ # required privileges for each action by providing a hash or an array of
73
+ # pairs. By default, for each action the action name is taken as privilege
74
+ # (action search in the example below requires the privilege :index
75
+ # :companies). Any controller action that is not specified and does not
76
+ # belong to the seven CRUD actions is handled as a member method.
77
+ # class CompanyController < ApplicationController
78
+ # filter_resource_access :collection => [[:search, :index], :index],
79
+ # :additional_member => {:mark_as_key_company => :update}
80
+ # end
81
+ # The +additional_+* options add to the respective CRUD actions,
82
+ # the other options (:+member+, :+collection+, :+new+) replace their
83
+ # respective CRUD actions.
84
+ # filter_resource_access :member => { :toggle_open => :update }
85
+ # Would declare :toggle_open as the only member action in the controller and
86
+ # require that permission :update is granted for the current user.
87
+ # filter_resource_access :additional_member => { :toggle_open => :update }
88
+ # Would add a member action :+toggle_open+ to the default members, such as :+show+.
89
+ #
90
+ # If :+collection+ is an array of method names filter_resource_access will
91
+ # associate a permission with the method that is the same as the method
92
+ # name and no attribute checks will be performed unless
93
+ # :attribute_check => true
94
+ # is added in the options.
95
+ #
96
+ # You can override the default object loading by implementing any of the
97
+ # following instance methods on the controller. Examples are given for the
98
+ # BranchController (with +nested_in+ set to :+companies+):
99
+ # [+new_branch_from_params+]
100
+ # Used for +new+ actions.
101
+ # [+new_branch_for_collection+]
102
+ # Used for +collection+ actions if the +nested_in+ option is set.
103
+ # [+load_branch+]
104
+ # Used for +member+ actions.
105
+ # [+load_company+]
106
+ # Used for all +new+, +member+, and +collection+ actions if the
107
+ # +nested_in+ option is set.
108
+ #
109
+ # All options:
110
+ # [:+member+]
111
+ # Member methods are actions like +show+, which have an params[:id] from
112
+ # which to load the controller object and assign it to @controller_name,
113
+ # e.g. @+branch+.
114
+ #
115
+ # By default, member actions are [:+show+, :+edit+, :+update+,
116
+ # :+destroy+]. Also, any action not belonging to the seven CRUD actions
117
+ # are handled as member actions.
118
+ #
119
+ # There are three different syntax to specify member, collection and
120
+ # new actions.
121
+ # * Hash: Lets you set the required privilege for each action:
122
+ # {:+show+ => :+show+, :+mark_as_important+ => :+update+}
123
+ # * Array of actions or pairs: [:+show+, [:+mark_as_important+, :+update+]],
124
+ # with single actions requiring the privilege of the same name as the method.
125
+ # * Single method symbol: :+show+
126
+ # [:+additional_member+]
127
+ # Allows to add additional member actions to the default resource +member+
128
+ # actions.
129
+ # [:+collection+]
130
+ # Collection actions are like :+index+, actions without any controller object
131
+ # to check attributes of. If +nested_in+ is given, a new object is
132
+ # created from the parent object, e.g. @company.branches.new. Without
133
+ # +nested_in+, attribute check is deactivated for these actions. By
134
+ # default, collection is set to :+index+.
135
+ # [:+additional_collection+]
136
+ # Allows to add additional collection actions to the default resource +collection+
137
+ # actions.
138
+ # [:+new+]
139
+ # +new+ methods are actions such as +new+ and +create+, which don't
140
+ # receive a params[:id] to load an object from, but
141
+ # a params[:controller_name_singular] hash with attributes for a new
142
+ # object. The attributes will be used here to create a new object and
143
+ # check the object against the authorization rules. The object is
144
+ # assigned to @controller_name_singular, e.g. @branch.
145
+ #
146
+ # If +nested_in+ is given, the new object
147
+ # is created from the parent_object.controller_name
148
+ # proxy, e.g. company.branches.new(params[:branch]). By default,
149
+ # +new+ is set to [:new, :create].
150
+ # [:+additional_new+]
151
+ # Allows to add additional new actions to the default resource +new+ actions.
152
+ # [:+context+]
153
+ # The context is used to determine the model to load objects from for the
154
+ # before_actions and the context of privileges to use in authorization
155
+ # checks.
156
+ # [:+nested_in+]
157
+ # Specifies the parent controller if the resource is nested in another
158
+ # one. This is used to automatically load the parent object, e.g.
159
+ # @+company+ from params[:company_id] for a BranchController nested in
160
+ # a CompanyController.
161
+ # [:+shallow+]
162
+ # Only relevant when used in conjunction with +nested_in+. Specifies a nested resource
163
+ # as being a shallow nested resource, resulting in the controller not attempting to
164
+ # load a parent object for all member actions defined by +member+ and
165
+ # +additional_member+ or rather the default member actions (:+show+, :+edit+,
166
+ # :+update+, :+destroy+).
167
+ # [:+no_attribute_check+]
168
+ # Allows to set actions for which no attribute check should be performed.
169
+ # See filter_access_to on details. By default, with no +nested_in+,
170
+ # +no_attribute_check+ is set to all collections. If +nested_in+ is given
171
+ # +no_attribute_check+ is empty by default.
172
+ # [:+strong_parameters+]
173
+ # If set to true, relies on controller to provide instance variable and
174
+ # create new object in :create action. Set true if you use strong_params
175
+ # and false if you use protected_attributes.
176
+ #
177
+ def filter_resource_access(options = {})
178
+ options = {
179
+ :new => [:new, :create],
180
+ :additional_new => nil,
181
+ :member => [:show, :edit, :update, :destroy],
182
+ :additional_member => nil,
183
+ :collection => [:index],
184
+ :additional_collection => nil,
185
+ #:new_method_for_collection => nil, # only symbol method name
186
+ #:new_method => nil, # only symbol method name
187
+ #:load_method => nil, # only symbol method name
188
+ :no_attribute_check => nil,
189
+ :context => nil,
190
+ :model => nil,
191
+ :nested_in => nil,
192
+ :strong_parameters => nil
193
+ }.merge(options)
194
+ options.merge!({ :strong_parameters => true }) if options[:strong_parameters] == nil
195
+
196
+ new_actions = actions_from_option( options[:new] ).merge(
197
+ actions_from_option(options[:additional_new]) )
198
+ members = actions_from_option(options[:member]).merge(
199
+ actions_from_option(options[:additional_member]))
200
+ collections = actions_from_option(options[:collection]).merge(
201
+ actions_from_option(options[:additional_collection]))
202
+
203
+ no_attribute_check_actions = options[:strong_parameters] ? actions_from_option(options[:collection]).merge(actions_from_option([:create])) : collections
204
+
205
+ options[:no_attribute_check] ||= no_attribute_check_actions.keys unless options[:nested_in]
206
+
207
+ unless options[:nested_in].blank?
208
+ load_parent_method = :"load_#{options[:nested_in].to_s.singularize}"
209
+ shallow_exceptions = options[:shallow] ? {:except => members.keys} : {}
210
+ before_action shallow_exceptions do |controller|
211
+ if controller.respond_to?(load_parent_method, true)
212
+ controller.send(load_parent_method)
213
+ else
214
+ controller.send(:load_parent_controller_object, options[:nested_in])
215
+ end
216
+ end
217
+
218
+ new_for_collection_method = :"new_#{controller_name.singularize}_for_collection"
219
+ before_action :only => collections.keys do |controller|
220
+ # new_for_collection
221
+ if controller.respond_to?(new_for_collection_method, true)
222
+ controller.send(new_for_collection_method)
223
+ else
224
+ controller.send(:new_controller_object_for_collection,
225
+ options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
226
+ end
227
+ end
228
+ end
229
+
230
+ unless options[:strong_parameters]
231
+ new_from_params_method = :"new_#{controller_name.singularize}_from_params"
232
+ before_action :only => new_actions.keys do |controller|
233
+ # new_from_params
234
+ if controller.respond_to?(new_from_params_method, true)
235
+ controller.send(new_from_params_method)
236
+ else
237
+ controller.send(:new_controller_object_from_params,
238
+ options[:context] || controller_name, options[:nested_in], options[:strong_parameters])
239
+ end
240
+ end
241
+ else
242
+ new_object_method = :"new_#{controller_name.singularize}"
243
+ before_action :only => :new do |controller|
244
+ # new_from_params
245
+ if controller.respond_to?(new_object_method, true)
246
+ controller.send(new_object_method)
247
+ else
248
+ controller.send(:new_blank_controller_object,
249
+ options[:context] || controller_name, options[:nested_in], options[:strong_parameters], options[:model])
250
+ end
251
+ end
252
+ end
253
+
254
+ load_method = :"load_#{controller_name.singularize}"
255
+ before_action :only => members.keys do |controller|
256
+ # load controller object
257
+ if controller.respond_to?(load_method, true)
258
+ controller.send(load_method)
259
+ else
260
+ controller.send(:load_controller_object, options[:context] || controller_name, options[:model])
261
+ end
262
+ end
263
+ filter_access_to :all, :attribute_check => true, :context => options[:context], :model => options[:model]
264
+
265
+ members.merge(new_actions).merge(collections).each do |action, privilege|
266
+ if action != privilege or (options[:no_attribute_check] and options[:no_attribute_check].include?(action))
267
+ filter_options = {
268
+ :strong_parameters => options[:strong_parameters],
269
+ :context => options[:context],
270
+ :attribute_check => !options[:no_attribute_check] || !options[:no_attribute_check].include?(action),
271
+ :model => options[:model]
272
+ }
273
+ filter_options[:require] = privilege if action != privilege
274
+ filter_access_to(action, filter_options)
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ protected
281
+
282
+ def filter_access_filter # :nodoc:
283
+ unless allowed?(action_name)
284
+ if respond_to?(:permission_denied, true)
285
+ # permission_denied needs to render or redirect
286
+ send(:permission_denied)
287
+ else
288
+ render plain: 'You are not allowed to access this action.', status: :forbidden
289
+ end
290
+ end
291
+ end
292
+
293
+ def load_controller_object(context_without_namespace = nil, model = nil) # :nodoc:
294
+ instance_var = :"@#{context_without_namespace.to_s.singularize}"
295
+ model = model ? model.classify.constantize : context_without_namespace.to_s.classify.constantize
296
+ instance_variable_set(instance_var, model.find(params[:id]))
297
+ end
298
+
299
+ def load_parent_controller_object(parent_context_without_namespace) # :nodoc:
300
+ instance_var = :"@#{parent_context_without_namespace.to_s.singularize}"
301
+ model = parent_context_without_namespace.to_s.classify.constantize
302
+ instance_variable_set(instance_var, model.find(params[:"#{parent_context_without_namespace.to_s.singularize}_id"]))
303
+ end
304
+
305
+ def new_controller_object_from_params(context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
306
+ model_or_proxy = parent_context_without_namespace ?
307
+ instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
308
+ context_without_namespace.to_s.classify.constantize
309
+ instance_var = :"@#{context_without_namespace.to_s.singularize}"
310
+ instance_variable_set(instance_var,
311
+ model_or_proxy.new(params[context_without_namespace.to_s.singularize]))
312
+ end
313
+
314
+ def new_blank_controller_object(context_without_namespace, parent_context_without_namespace, strong_params, model) # :nodoc:
315
+ if model
316
+ model_or_proxy = model.to_s.classify.constantize
317
+ else
318
+ model_or_proxy = parent_context_without_namespace ?
319
+ instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
320
+ context_without_namespace.to_s.classify.constantize
321
+ end
322
+ instance_var = :"@#{context_without_namespace.to_s.singularize}"
323
+ instance_variable_set(instance_var,
324
+ model_or_proxy.new())
325
+ end
326
+
327
+ def new_controller_object_for_collection(context_without_namespace, parent_context_without_namespace, strong_params) # :nodoc:
328
+ model_or_proxy = parent_context_without_namespace ?
329
+ instance_variable_get(:"@#{parent_context_without_namespace.to_s.singularize}").send(context_without_namespace.to_sym) :
330
+ context_without_namespace.to_s.classify.constantize
331
+ instance_var = :"@#{context_without_namespace.to_s.singularize}"
332
+ instance_variable_set(instance_var, model_or_proxy.new)
333
+ end
334
+
335
+ def api_class
336
+ self.class
337
+ end
338
+ end
339
+ end
340
+ end