Skip to content
Newer
Older
100644 314 lines (300 sloc) 12.2 KB
44b36ce @ryanb adding controller additions with basic behavior.
authored
1 module CanCan
dfd84a1 @ryanb improving inline documentation
authored
2
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
3 # This module is automatically included into all controllers.
4 # It also makes the "can?" and "cannot?" methods available to all views.
44b36ce @ryanb adding controller additions with basic behavior.
authored
5 module ControllerAdditions
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
6 module ClassMethods
ffa677b @ryanb Don't set resource instance variable if it has been set already - clo…
authored
7 # Sets up a before filter which loads and authorizes the current resource. This performs both
8 # load_resource and authorize_resource and accepts the same arguments. See those methods for details.
dfd84a1 @ryanb improving inline documentation
authored
9 #
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
10 # class BooksController < ApplicationController
11 # load_and_authorize_resource
12 # end
dfd84a1 @ryanb improving inline documentation
authored
13 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
14 def load_and_authorize_resource(*args)
4eee637 @ryanb adding support for loading through Inherited Resources - closes #23
authored
15 cancan_resource_class.add_before_filter(self, :load_and_authorize_resource, *args)
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
16 end
dfd84a1 @ryanb improving inline documentation
authored
17
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
18 # Sets up a before filter which loads the model resource into an instance variable.
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
19 # For example, given an ArticlesController it will load the current article into the @article
20 # instance variable. It does this by either calling Article.find(params[:id]) or
6c3e87e @ryanb updating readme and documentation
authored
21 # Article.new(params[:article]) depending upon the action. The index action will
22 # automatically set @articles to Article.accessible_by(current_ability).
dfd84a1 @ryanb improving inline documentation
authored
23 #
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
24 # If a conditions hash is used in the Ability, the +new+ and +create+ actions will set
25 # the initial attributes based on these conditions. This way these actions will satisfy
26 # the ability restrictions.
27 #
ffa677b @ryanb Don't set resource instance variable if it has been set already - clo…
authored
28 # Call this method directly on the controller class.
dfd84a1 @ryanb improving inline documentation
authored
29 #
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
30 # class BooksController < ApplicationController
31 # load_resource
32 # end
dfd84a1 @ryanb improving inline documentation
authored
33 #
ffa677b @ryanb Don't set resource instance variable if it has been set already - clo…
authored
34 # A resource is not loaded if the instance variable is already set. This makes it easy to override
35 # the behavior through a before_filter on certain actions.
dfd84a1 @ryanb improving inline documentation
authored
36 #
ffa677b @ryanb Don't set resource instance variable if it has been set already - clo…
authored
37 # class BooksController < ApplicationController
38 # before_filter :find_book_by_permalink, :only => :show
39 # load_resource
40 #
41 # private
42 #
43 # def find_book_by_permalink
44 # @book = Book.find_by_permalink!(params[:id)
45 # end
46 # end
dfd84a1 @ryanb improving inline documentation
authored
47 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
48 # If a name is provided which does not match the controller it assumes it is a parent resource. Child
49 # resources can then be loaded through it.
50 #
51 # class BooksController < ApplicationController
52 # load_resource :author
53 # load_resource :book, :through => :author
54 # end
55 #
56 # Here the author resource will be loaded before each action using params[:author_id]. The book resource
57 # will then be loaded through the @author instance variable.
58 #
59 # That first argument is optional and will default to the singular name of the controller.
60 # A hash of options (see below) can also be passed to this method to further customize it.
61 #
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
62 # See load_and_authorize_resource to automatically authorize the resource too.
dfd84a1 @ryanb improving inline documentation
authored
63 #
63634b4 @ryanb Adding :collection and :new options to load_resource method so we can…
authored
64 # Options:
94e031b @ryanb Pass :only and :except options to before filters for load/authorize r…
authored
65 # [:+only+]
66 # Only applies before filter to given actions.
dfd84a1 @ryanb improving inline documentation
authored
67 #
94e031b @ryanb Pass :only and :except options to before filters for load/authorize r…
authored
68 # [:+except+]
69 # Does not apply before filter to given actions.
dfd84a1 @ryanb improving inline documentation
authored
70 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
71 # [:+through+]
c11ffb6 @ryanb support loading resource :through method along with instance variable…
authored
72 # Load this resource through another one. This should match the name of the parent instance variable or method.
2a3dd85 @ryanb adding :name option to load_and_authorize_resource if it does not mat…
authored
73 #
92995d7 @ryanb adding :through_association option to load_resource (thanks hunterae)…
authored
74 # [:+through_association+]
75 # The name of the association to fetch the child records through the parent resource. This is normally not needed
76 # because it defaults to the pluralized resource name.
77 #
264e2d2 @ryanb raise AccessDenied error when loading child while parent is nil, pass…
authored
78 # [:+shallow+]
79 # Pass +true+ to allow this resource to be loaded directly when parent is +nil+. Defaults to +false+.
80 #
c9e0f4e @ryanb renaming :singular resource option to :singleton
authored
81 # [:+singleton+]
82 # Pass +true+ if this is a singleton resource through a +has_one+ association.
84f4c90 @ryanb adding :singular option to support has_one associations in load/autho…
authored
83 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
84 # [:+parent+]
85 # True or false depending on if the resource is considered a parent resource. This defaults to +true+ if a resource
86 # name is given which does not match the controller.
2a3dd85 @ryanb adding :name option to load_and_authorize_resource if it does not mat…
authored
87 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
88 # [:+class+]
23a5888 @ryanb renaming :class option to :resource for load_and_authorize_resource w…
authored
89 # The class to use for the model (string or constant).
dfd84a1 @ryanb improving inline documentation
authored
90 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
91 # [:+instance_name+]
92 # The name of the instance variable to load the resource into.
93 #
236cece @ryanb adding :find_by option to load_resource - closes #19
authored
94 # [:+find_by+]
95 # Find using a different attribute other than id. For example.
96 #
97 # load_resource :find_by => :permalink # will use find_by_permlink!(params[:id])
98 #
63634b4 @ryanb Adding :collection and :new options to load_resource method so we can…
authored
99 # [:+collection+]
100 # Specify which actions are resource collection actions in addition to :+index+. This
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
101 # is usually not necessary because it will try to guess depending on if the id param is present.
dfd84a1 @ryanb improving inline documentation
authored
102 #
63634b4 @ryanb Adding :collection and :new options to load_resource method so we can…
authored
103 # load_resource :collection => [:sort, :list]
dfd84a1 @ryanb improving inline documentation
authored
104 #
63634b4 @ryanb Adding :collection and :new options to load_resource method so we can…
authored
105 # [:+new+]
106 # Specify which actions are new resource actions in addition to :+new+ and :+create+.
107 # Pass an action name into here if you would like to build a new resource instead of
108 # fetch one.
dfd84a1 @ryanb improving inline documentation
authored
109 #
63634b4 @ryanb Adding :collection and :new options to load_resource method so we can…
authored
110 # load_resource :new => :build
dfd84a1 @ryanb improving inline documentation
authored
111 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
112 def load_resource(*args)
4eee637 @ryanb adding support for loading through Inherited Resources - closes #23
authored
113 cancan_resource_class.add_before_filter(self, :load_resource, *args)
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
114 end
dfd84a1 @ryanb improving inline documentation
authored
115
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
116 # Sets up a before filter which authorizes the resource using the instance variable.
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
117 # For example, if you have an ArticlesController it will check the @article instance variable
118 # and ensure the user can perform the current action on it. Under the hood it is doing
119 # something like the following.
dfd84a1 @ryanb improving inline documentation
authored
120 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
121 # authorize!(params[:action].to_sym, @article || Article)
dfd84a1 @ryanb improving inline documentation
authored
122 #
ffa677b @ryanb Don't set resource instance variable if it has been set already - clo…
authored
123 # Call this method directly on the controller class.
dfd84a1 @ryanb improving inline documentation
authored
124 #
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
125 # class BooksController < ApplicationController
126 # authorize_resource
127 # end
dfd84a1 @ryanb improving inline documentation
authored
128 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
129 # If you pass in the name of a resource which does not match the controller it will assume
130 # it is a parent resource.
131 #
132 # class BooksController < ApplicationController
133 # authorize_resource :author
134 # authorize_resource :book
135 # end
136 #
137 # Here it will authorize :+show+, @+author+ on every action before authorizing the book.
138 #
139 # That first argument is optional and will default to the singular name of the controller.
140 # A hash of options (see below) can also be passed to this method to further customize it.
141 #
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
142 # See load_and_authorize_resource to automatically load the resource too.
dfd84a1 @ryanb improving inline documentation
authored
143 #
94e031b @ryanb Pass :only and :except options to before filters for load/authorize r…
authored
144 # Options:
145 # [:+only+]
146 # Only applies before filter to given actions.
dfd84a1 @ryanb improving inline documentation
authored
147 #
94e031b @ryanb Pass :only and :except options to before filters for load/authorize r…
authored
148 # [:+except+]
149 # Does not apply before filter to given actions.
dfd84a1 @ryanb improving inline documentation
authored
150 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
151 # [:+parent+]
152 # True or false depending on if the resource is considered a parent resource. This defaults to +true+ if a resource
153 # name is given which does not match the controller.
154 #
155 # [:+class+]
156 # The class to use for the model (string or constant). This passed in when the instance variable is not set.
157 # Pass +false+ if there is no associated class for this resource and it will use a symbol of the resource name.
2a3dd85 @ryanb adding :name option to load_and_authorize_resource if it does not mat…
authored
158 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
159 # [:+instance_name+]
160 # The name of the instance variable for this resource.
2a3dd85 @ryanb adding :name option to load_and_authorize_resource if it does not mat…
authored
161 #
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
162 # [:+through+]
163 # Authorize conditions on this parent resource when instance isn't available.
164 #
25a1c55 @ryanb adding :through option to replace :nesting option and moving Resource…
authored
165 def authorize_resource(*args)
4eee637 @ryanb adding support for loading through Inherited Resources - closes #23
authored
166 cancan_resource_class.add_before_filter(self, :authorize_resource, *args)
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
167 end
1af6c6f @ryanb adding check_authorization and skip_authorization controller class me…
authored
168
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
169 # Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
170 # If neither of these authorization methods are called, a CanCan::AuthorizationNotPerformed exception will be raised.
171 # This is normally added to the ApplicationController to ensure all controller actions do authorization.
172 #
173 # class ApplicationController < ActionController::Base
174 # check_authorization
175 # end
176 #
177 # Any arguments are passed to the +after_filter+ it triggers.
178 #
179 # See skip_authorization to bypass this check on specific controller actions.
1af6c6f @ryanb adding check_authorization and skip_authorization controller class me…
authored
180 def check_authorization(*args)
181 self.after_filter(*args) do |controller|
182 unless controller.instance_variable_defined?(:@_authorized)
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
183 raise AuthorizationNotPerformed, "This action failed the check_authorization because it does not authorize_resource. Add skip_authorization to bypass this check."
1af6c6f @ryanb adding check_authorization and skip_authorization controller class me…
authored
184 end
185 end
186 end
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
187
188 # Call this in the class of a controller to skip the check_authorization behavior on the actions.
189 #
190 # class HomeController < ApplicationController
191 # skip_authorization :only => :index
192 # end
193 #
194 # Any arguments are passed to the +before_filter+ it triggers.
195 def skip_authorization(*args)
196 self.before_filter(*args) do |controller|
197 controller.instance_variable_set(:@_authorized, true)
198 end
199 end
4eee637 @ryanb adding support for loading through Inherited Resources - closes #23
authored
200
201 def cancan_resource_class
202 if ancestors.map(&:to_s).include? "InheritedResources::Actions"
203 InheritedResource
204 else
205 ControllerResource
206 end
207 end
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
208 end
dfd84a1 @ryanb improving inline documentation
authored
209
44b36ce @ryanb adding controller additions with basic behavior.
authored
210 def self.included(base)
a5f9882 @ryanb turning load and authorize resource methods into class methods which …
authored
211 base.extend ClassMethods
0f49b54 @ryanb adding 'cannot?' method which performs opposite check of 'can?' - clo…
authored
212 base.helper_method :can?, :cannot?
44b36ce @ryanb adding controller additions with basic behavior.
authored
213 end
dfd84a1 @ryanb improving inline documentation
authored
214
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
215 # Raises a CanCan::AccessDenied exception if the current_ability cannot
216 # perform the given action. This is usually called in a controller action or
217 # before filter to perform the authorization.
dfd84a1 @ryanb improving inline documentation
authored
218 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
219 # def show
220 # @article = Article.find(params[:id])
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
221 # authorize! :read, @article
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
222 # end
dfd84a1 @ryanb improving inline documentation
authored
223 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
224 # A :message option can be passed to specify a different message.
dfd84a1 @ryanb improving inline documentation
authored
225 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
226 # authorize! :read, @article, :message => "Not authorized to read #{@article.name}"
dfd84a1 @ryanb improving inline documentation
authored
227 #
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
228 # You can also use I18n to customize the message. Action aliases defined in Ability work here.
229 #
230 # en:
231 # unauthorized:
232 # manage:
6c3e87e @ryanb updating readme and documentation
authored
233 # all: "Not authorized to %{action} %{subject}."
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
234 # user: "Not allowed to manage other user accounts."
235 # update:
236 # project: "Not allowed to update this project."
237 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
238 # You can rescue from the exception in the controller to customize how unauthorized
239 # access is displayed to the user.
dfd84a1 @ryanb improving inline documentation
authored
240 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
241 # class ApplicationController < ActionController::Base
ef22de6 @ryanb adding custom message argument to unauthorized! method - closes #18
authored
242 # rescue_from CanCan::AccessDenied do |exception|
243 # flash[:error] = exception.message
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
244 # redirect_to root_url
245 # end
246 # end
dfd84a1 @ryanb improving inline documentation
authored
247 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
248 # See the CanCan::AccessDenied exception for more details on working with the exception.
dfd84a1 @ryanb improving inline documentation
authored
249 #
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
250 # See the load_and_authorize_resource method to automatically add the authorize! behavior
251 # to the default RESTful actions.
a5f838a @ryanb use I18n for unauthorization messages - closes #103
authored
252 def authorize!(*args)
1af6c6f @ryanb adding check_authorization and skip_authorization controller class me…
authored
253 @_authorized = true
a5f838a @ryanb use I18n for unauthorization messages - closes #103
authored
254 current_ability.authorize!(*args)
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
255 end
dfd84a1 @ryanb improving inline documentation
authored
256
8903fee @ryanb removing unauthorized! in favor of authorize! and including more info…
authored
257 def unauthorized!(message = nil)
258 raise ImplementationRemoved, "The unauthorized! method has been removed from CanCan, use authorize! instead."
44b36ce @ryanb adding controller additions with basic behavior.
authored
259 end
dfd84a1 @ryanb improving inline documentation
authored
260
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
261 # Creates and returns the current user's ability and caches it. If you
262 # want to override how the Ability is defined then this is the place.
263 # Just define the method in the controller to change behavior.
dfd84a1 @ryanb improving inline documentation
authored
264 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
265 # def current_ability
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
266 # # instead of Ability.new(current_user)
267 # @current_ability ||= UserAbility.new(current_account)
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
268 # end
dfd84a1 @ryanb improving inline documentation
authored
269 #
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
270 # Notice it is important to cache the ability object so it is not
271 # recreated every time.
44b36ce @ryanb adding controller additions with basic behavior.
authored
272 def current_ability
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
273 @current_ability ||= ::Ability.new(current_user)
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
274 end
dfd84a1 @ryanb improving inline documentation
authored
275
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
276 # Use in the controller or view to check the user's permission for a given action
277 # and object.
dfd84a1 @ryanb improving inline documentation
authored
278 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
279 # can? :destroy, @project
dfd84a1 @ryanb improving inline documentation
authored
280 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
281 # You can also pass the class instead of an instance (if you don't have one handy).
dfd84a1 @ryanb improving inline documentation
authored
282 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
283 # <% if can? :create, Project %>
284 # <%= link_to "New Project", new_project_path %>
285 # <% end %>
dfd84a1 @ryanb improving inline documentation
authored
286 #
bf9b8ad @ryanb filling in some inline documentation for 1.4
authored
287 # If it's a nested resource, you can pass the parent instance in a hash. This way it will
288 # check conditions which reach through that association.
289 #
290 # <% if can? :create, @category => Project %>
291 # <%= link_to "New Project", new_project_path %>
292 # <% end %>
293 #
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
294 # This simply calls "can?" on the current_ability. See Ability#can?.
44b36ce @ryanb adding controller additions with basic behavior.
authored
295 def can?(*args)
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
296 current_ability.can?(*args)
44b36ce @ryanb adding controller additions with basic behavior.
authored
297 end
dfd84a1 @ryanb improving inline documentation
authored
298
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
299 # Convenience method which works the same as "can?" but returns the opposite value.
dfd84a1 @ryanb improving inline documentation
authored
300 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
301 # cannot? :destroy, @project
dfd84a1 @ryanb improving inline documentation
authored
302 #
0f49b54 @ryanb adding 'cannot?' method which performs opposite check of 'can?' - clo…
authored
303 def cannot?(*args)
ef5900c @ryanb adding caching to current_ability class method, if you're overriding …
authored
304 current_ability.cannot?(*args)
0f49b54 @ryanb adding 'cannot?' method which performs opposite check of 'can?' - clo…
authored
305 end
44b36ce @ryanb adding controller additions with basic behavior.
authored
306 end
307 end
308
aaed265 @ryanb turning into a funtioning Rails plugin
authored
309 if defined? ActionController
310 ActionController::Base.class_eval do
311 include CanCan::ControllerAdditions
312 end
1edf583 @ryanb BACKWARDS INCOMPATIBLE: use Ability#initialize instead of 'prepare' t…
authored
313 end
Something went wrong with that request. Please try again.