ae_declarative_authorization 0.8.0 → 0.9.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.
@@ -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