Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 232 lines (217 sloc) 8.186 kb
0cfb8c7 @ryanb adding basic ability module
authored
1 module CanCan
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
2
3 # This module is designed to be included into an Ability class. This will
4 # provide the "can" methods for defining and checking abilities.
5 #
6 # class Ability
7 # include CanCan::Ability
8 #
9 # def initialize(user)
10 # if user.admin?
11 # can :manage, :all
12 # else
13 # can :read, :all
14 # end
15 # end
16 # end
17 #
0cfb8c7 @ryanb adding basic ability module
authored
18 module Ability
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
19 # Use to check the user's permission for a given action and object.
20 #
21 # can? :destroy, @project
22 #
23 # You can also pass the class instead of an instance (if you don't have one handy).
24 #
25 # can? :create, Project
26 #
510cf50 @ryanb adding documentation for passing additional arguments to can?
authored
27 # Any additional arguments will be passed into the "can" block definition. This
28 # can be used to pass more information about the user's request for example.
29 #
30 # can? :create, Project, request.remote_ip
31 #
32 # can :create Project do |project, remote_ip|
33 # # ...
34 # end
35 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
36 # Not only can you use the can? method in the controller and view (see ControllerAdditions),
37 # but you can also call it directly on an ability instance.
38 #
39 # ability.can? :destroy, @project
40 #
41 # This makes testing a user's abilities very easy.
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
42 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
43 # def test "user can only destroy projects which he owns"
44 # user = User.new
45 # ability = Ability.new(user)
46 # assert ability.can?(:destroy, Project.new(:user => user))
47 # assert ability.cannot?(:destroy, Project.new)
48 # end
49 #
d9f3c8b @ryanb renaming noun to subject internally
authored
50 def can?(action, subject, *extra_args)
bbbc8a6 @ryanb refactoring much of Ability class into separate CanDefinition class
authored
51 can_definition = matching_can_definition(action, subject)
52 can_definition && can_definition.can?(action, subject, extra_args)
0cfb8c7 @ryanb adding basic ability module
authored
53 end
54
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
55 # Convenience method which works the same as "can?" but returns the opposite value.
56 #
57 # cannot? :destroy, @project
58 #
0f49b54 @ryanb adding 'cannot?' method which performs opposite check of 'can?' - clo…
authored
59 def cannot?(*args)
60 !can?(*args)
61 end
62
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
63 # Defines which abilities are allowed using two arguments. The first one is the action
64 # you're setting the permission for, the second one is the class of object you're setting it on.
65 #
66 # can :update, Article
67 #
68 # You can pass an array for either of these parameters to match any one.
69 #
70 # can [:update, :destroy], [Article, Comment]
71 #
72 # In this case the user has the ability to update or destroy both articles and comments.
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
73 #
74 # You can pass a hash of conditions as the third argument.
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
75 #
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
76 # can :read, Project, :active => true, :user_id => user.id
77 #
78 # Here the user can only see active projects which he owns. See ControllerAdditions#conditions for a way to
79 # use this in database queries.
80 #
81 # If the conditions hash does not give you enough control over defining abilities, you can use a block to
82 # write any Ruby code you want.
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
83 #
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
84 # can :update, Project do |project|
85 # project && project.groups.include?(user.group)
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
86 # end
87 #
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
88 # If the block returns true then the user has that :update ability for that project, otherwise he
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
89 # will be denied access. It's possible for the passed in model to be nil if one isn't specified,
90 # so be sure to take that into consideration.
91 #
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
92 # The downside to using a block is that it cannot be used to generate conditions for database queries.
93 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
94 # You can pass :all to reference every type of object. In this case the object type will be passed
95 # into the block as well (just in case object is nil).
96 #
97 # can :read, :all do |object_class, object|
98 # object_class != Order
99 # end
100 #
101 # Here the user has permission to read all objects except orders.
102 #
103 # You can also pass :manage as the action which will match any action. In this case the action is
104 # passed to the block.
105 #
106 # can :manage, Comment do |action, comment|
107 # action != :destroy
108 # end
109 #
e603655 @ryanb support custom objects (usually symbols) in can definition - closes #8
authored
110 # You can pass custom objects into this "can" method, this is usually done through a symbol
111 # and is useful if a class isn't available to define permissions on.
112 #
113 # can :read, :stats
114 # can? :read, :stats # => true
115 #
d9f3c8b @ryanb renaming noun to subject internally
authored
116 def can(action, subject, conditions = nil, &block)
bbbc8a6 @ryanb refactoring much of Ability class into separate CanDefinition class
authored
117 can_definitions << CanDefinition.new(true, action, subject, conditions, block)
d4405e6 @ryanb adding cannot method to define which abilities cannot be done - close…
authored
118 end
119
120 # Define an ability which cannot be done. Accepts the same arguments as "can".
121 #
122 # can :read, :all
123 # cannot :read, Comment
124 #
125 # A block can be passed just like "can", however if the logic is complex it is recommended
126 # to use the "can" method.
127 #
128 # cannot :read, Product do |product|
129 # product.invisible?
130 # end
131 #
d9f3c8b @ryanb renaming noun to subject internally
authored
132 def cannot(action, subject, conditions = nil, &block)
bbbc8a6 @ryanb refactoring much of Ability class into separate CanDefinition class
authored
133 can_definitions << CanDefinition.new(false, action, subject, conditions, block)
4b6f538 @ryanb moving can definition into ability instance instead of class, this re…
authored
134 end
135
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
136 # Alias one or more actions into another one.
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
137 #
138 # alias_action :update, :destroy, :to => :modify
139 # can :modify, Comment
140 #
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
141 # Then :modify permission will apply to both :update and :destroy requests.
142 #
143 # can? :update, Comment # => true
144 # can? :destroy, Comment # => true
145 #
146 # This only works in one direction. Passing the aliased action into the "can?" call
147 # will not work because aliases are meant to generate more generic actions.
148 #
149 # alias_action :update, :destroy, :to => :modify
150 # can :update, Comment
151 # can? :modify, Comment # => false
152 #
153 # Unless that exact alias is used.
154 #
155 # can :modify, Comment
156 # can? :modify, Comment # => true
157 #
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
158 # The following aliases are added by default for conveniently mapping common controller actions.
159 #
160 # alias_action :index, :show, :to => :read
161 # alias_action :new, :to => :create
162 # alias_action :edit, :to => :update
163 #
5bd1a85 @ryanb little fixes to inline documentation (rdocs)
authored
164 # This way one can use params[:action] in the controller to determine the permission.
4b6f538 @ryanb moving can definition into ability instance instead of class, this re…
authored
165 def alias_action(*args)
166 target = args.pop[:to]
f99d506 @ryanb Append aliased actions (don't overwrite them) - closes #20
authored
167 aliased_actions[target] ||= []
168 aliased_actions[target] += args
4b6f538 @ryanb moving can definition into ability instance instead of class, this re…
authored
169 end
170
7d3b4cd @ryanb Adding clear_aliased_actions to Ability which removes previously defi…
authored
171 # Returns a hash of aliased actions. The key is the target and the value is an array of actions aliasing the key.
c40490d @ryanb refactoring ability can? method - closes #12
authored
172 def aliased_actions
173 @aliased_actions ||= default_alias_actions
174 end
175
7d3b4cd @ryanb Adding clear_aliased_actions to Ability which removes previously defi…
authored
176 # Removes previously aliased actions including the defaults.
177 def clear_aliased_actions
178 @aliased_actions = {}
179 end
180
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
181 # Returns a hash of conditions which match the given ability. This is useful if you need to generate a database
182 # query based on the current ability.
183 #
184 # can :read, Article, :visible => true
185 # conditions :read, Article # returns { :visible => true }
186 #
240c281 @ryanb renaming ActiveRecordAdditions#can method to accessible_by since it f…
authored
187 # Normally you will not call this method directly, but instead go through ActiveRecordAdditions#accessible_by method.
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
188 #
189 # If the ability is not defined then false is returned so be sure to take that into consideration.
190 # If the ability is defined using a block then this will raise an exception since a hash of conditions cannot be
191 # determined from that.
d9f3c8b @ryanb renaming noun to subject internally
authored
192 def conditions(action, subject)
bbbc8a6 @ryanb refactoring much of Ability class into separate CanDefinition class
authored
193 can_definition = matching_can_definition(action, subject)
194 if can_definition
195 raise Error, "Cannot determine ability conditions from block for #{action.inspect} #{subject.inspect}" if can_definition.block
196 can_definition.conditions || {}
197 else
198 false
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
199 end
200 end
201
7d3b4cd @ryanb Adding clear_aliased_actions to Ability which removes previously defi…
authored
202 private
203
bbbc8a6 @ryanb refactoring much of Ability class into separate CanDefinition class
authored
204 def can_definitions
205 @can_definitions ||= []
206 end
207
208 def matching_can_definition(action, subject)
209 can_definitions.reverse.detect do |can_definition|
210 can_definition.expand_actions(aliased_actions)
211 can_definition.matches? action, subject
baeef0b @ryanb adding conditions behavior to Ability#can and fetch with Ability#cond…
authored
212 end
213 end
214
4b6f538 @ryanb moving can definition into ability instance instead of class, this re…
authored
215 def default_alias_actions
216 {
217 :read => [:index, :show],
218 :create => [:new],
219 :update => [:edit],
220 }
221 end
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
222
c40490d @ryanb refactoring ability can? method - closes #12
authored
223 def includes_action?(actions, action)
224 actions.include?(:manage) || actions.include?(action)
225 end
226
d9f3c8b @ryanb renaming noun to subject internally
authored
227 def includes_subject?(subjects, subject)
228 subjects.include?(:all) || subjects.include?(subject) || subjects.any? { |c| c.kind_of?(Class) && subject.kind_of?(c) }
b9227eb @ryanb adding a lot of inline documentation to code for rdocs
authored
229 end
0cfb8c7 @ryanb adding basic ability module
authored
230 end
231 end
Something went wrong with that request. Please try again.