popel-active_acl_plus 0.4.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,317 @@
1
+ =Active Access Control Lists Plus (ActiveAclPlus)
2
+ The ActiveAclPlus plugin implements a flexible, fast and easy to use generic access control system.
3
+
4
+ ==License
5
+ ActiveAclPlus is released under the LGPL[http://www.opensource.org/licenses/lgpl-license.php]
6
+ (Gnu Lesser General Public License) - see the included LICENSE file, too.
7
+
8
+ ==Features
9
+ * ease of use - uses polymorphic collections for associations.
10
+ * advanced design - uses SQL nested sets for inheritance, thus only needs a single DB query to decide on a permission request.
11
+ * scalable - there are no real benchmarks yet. But the system design is based on http://phpgacl.sourceforge.net, adding object orientation, polymorphism and two levels of caching. PhpGacl claims "A real-world working version with many added layers of complexity supports over 60,000 Accounts, 200 Groups and 300 ACO's." Tests on my dev notebook show 10 - 30 times performance improvements compared to active_rbac.
12
+ * caching - uses instance caching and optionally stores permission results in memcached using timeouts.
13
+ * flexible - grants simple (2D, like <code>current_user.has_permission?(User::LOGIN)</code>) and object level (3D, like <code>admin.has_permission?(Forum::ADMIN, :on => team_forum)</code>) permissions. Can assign and request permissions to and from every ActiveRecord model. No "hardcoded" permissions - all permissions can be assigned at runtime.
14
+ * grouping - permissions can be inherited at target and requester side through groups. Every model implementing an SQL nested set tree may be used as a group.
15
+ * ControllerAction model loader: It maps controller actions to the DB so they can be used in permission assignment. The access object is available via calling <code>current_action</code> on the controller.
16
+ * exchangeable DB interface: ActiveRecord and direct MySQL adapter available
17
+ * supports namespaced models and single table inheritance (STI)
18
+
19
+ ==Limitations
20
+ * At present only one grouping type per model is supported. This could be changed on request but I don't see a use case for it yet.
21
+ * The DBMS has to support subselects. So PostgreSQL, Sqllite and MySQL 5 work, but MySQL 4 does not.
22
+
23
+
24
+ ==Prerequisites/Installation
25
+ ActiveAclPlus uses the has_many_polymorphs[http://www.agilewebdevelopment.com/plugins/restful_authentication] plugin. Make shure you've got a recent version, old versions have bugs affecting active_acl_plus.
26
+ !!Be sure you have the paches for 2.1, see: http://rubyforge.org/forum/forum.php?thread_id=26041&forum_id=16450
27
+
28
+ ./script/plugin install git://github.com/popel/active_acl_plus.git
29
+
30
+ or
31
+
32
+ sudo gem install activeaclplus
33
+
34
+ or from a github repository.
35
+
36
+ If you want to use grouped access objects you should install some kind of nested_set plugin
37
+ (awesome_nested_set,better_nested_set,...).
38
+ ==Short summary
39
+ The ActiveAclPlus system consists of access objects, which can be organized by access groups,
40
+ that request privileges on each other. Allowing or denying access to a privilege is controlled by
41
+ ACL (access control list entry) objects. Access objects and access groups can be instances of arbitrary
42
+ ActiveRecord model classes enhanced by acts_as_access_object and acts_as_access_group. They are associated
43
+ to ACL entries via polymorphic associations.
44
+
45
+ ===Access objects
46
+ These are basically requesters and targets in the permission system, as for example a User or a Forum model object.
47
+ In this case a user could act as a requester ("do I have privilege Y?") or target ("does access object X have
48
+ privilege Y on me?"). A Forum would most certainly be only used as a target, but all access objects can theoretically
49
+ be used as both requesters and targets. Access objects use the acts_as_access_object macro inside their definition.
50
+ This registers the model with the ACL system and enhances it with methods like has_privilege?.
51
+
52
+ See: ActiveAcl::Acts::AccessObject::ClassMethods, ActiveAcl::Acts::AccessObject::InstanceMethods,
53
+ ActiveAcl::Acts::AccessObject::SingletonMethods, ActiveAcl::Acts::Grant
54
+
55
+
56
+ Every access object class can specify an association that is used as the "grouping type" to it. So User may declare
57
+ has_and_belongs_to_many :user_groups and use acts_as_access_object :grouped_by => :user_groups. You can use
58
+ a has_and_belongs_to_many or a belongs_to association for this. Common mapping attributes
59
+ (:join_table, :foreign_key etc.) are supported.
60
+
61
+ ===Access groups
62
+ The access group model needs to implement a tree with nested set semantics having a "left" and "right"
63
+ column, e.g. by using the built-in acts_as_nested_set or the much more recommended acts_as_betted_nested_set
64
+ plugin. Groups are declared with acts_as_access_group.
65
+
66
+ Groups may be used to specify inheritance hierarchies for permissions. So you could have a 'registered users'
67
+ group as a subgroup of the 'users' group and assign the privilege to log in to this group via an ACL entry.
68
+ Every user belonging to this group will now be granted the privilege to log in. Then you could add a subgroup
69
+ to registerd users, 'banned users', and deny the log in privilege for this group. Every user added to this
70
+ group would now be unable to log in, regardless of beeing in 'registered users' or not, as 'banned users' would
71
+ override the permission settings of 'registered users'.
72
+
73
+ ===Privileges
74
+ A privilege object is an object for the thing we wish to define a permission for. So User::LOGIN could be a
75
+ privilege object for checking a users permission to log in, while Forum::ADMIN might define administration rights on a forum.
76
+
77
+ A privilege object itself is little more than it’s name and id and is usually bound to a constant inside the
78
+ application, as it is not expected to change at runtime. Privileges are usually created by the developer in
79
+ the source code and not in the admin frontend, as creating new privilege objects that have no meaning (by code
80
+ that checks for them) would be pointless.
81
+
82
+ ===Access Control List (ACL) entries
83
+ ACL entries are the glue between all these objects, defining which requesters and requester groups have access
84
+ to which privileges, optionally defining target objects and target groups as well. ACL entries are organized by
85
+ ACL sections, for better overview in the admin screens.
86
+
87
+ ==Usage
88
+ ===In short
89
+ "No access defined" for a privilege evaluates to "deny". This may be overriden by an explicit "allow" or "deny".
90
+ Privileges are inherited in requestor and target groups, this means you can override them in subgroups again.
91
+ Privileges directly assigned to an object always supercede those assigned to groups.
92
+
93
+ ===Simple (2D) permissions
94
+ We want all registered users to be able to log in. We create the User model, the UserGroup model and the
95
+ User::LOGIN privilege object as described above. Then we create a new ACL entry, set 'allow' to true, add
96
+ the "registered users" group as requester group, User::LOGIN as privilege and we are done. Every user assigned
97
+ to "registered users" or a subgroup of it will now be granted access by calling <code>my_user.has_privilege?(User::LOGIN)</code>.
98
+
99
+ ====Simple permissions example
100
+
101
+ class UserGroup < ActiveRecord::Base
102
+ acts_as_nested_set
103
+ acts_as_access_group
104
+ has_and_belongs_to_many :users
105
+ end
106
+
107
+ class User < ActiveRecord::Base
108
+ has_and_belongs_to_many :user_groups
109
+ acts_as_access_object :grouped_by => :user_groups
110
+ privilege_const_set('LOGIN')
111
+ end
112
+
113
+ # assume 'registered_users' exists and users 'john' and 'dr_evil' are members of it but 'anonymous' is not.
114
+ registered_users = UserGroup.find_by_name('registered_users')
115
+
116
+ the hard way:
117
+ acl = ActiveAcl::Acl.create :section => ActiveAcl::AclSection.create(:description => 'users')
118
+
119
+ acl.allow = true # true is default
120
+ acl.privileges << User::LOGIN
121
+ acl.iname="login"
122
+ acl.save
123
+
124
+ acl.requester_groups << registered_users
125
+
126
+ or much easier:
127
+
128
+ registered_users.grant_privilege!(User::LOGIN,:section_name => 'users',:acl_name => 'login')
129
+
130
+ john.has_privilege?(User::LOGIN) #=> true
131
+ dr_evil.has_privilege?(User::LOGIN) #=> true
132
+
133
+ anonymous.has_privilege?(User::LOGIN) #=> false
134
+
135
+ ===Overriding permissions
136
+ We want to ban specific users from our site. We create another ACL entry, assign the User::LOGIN privilege
137
+ object, set 'allow' to false and then assign these users as requesters to the ACL entry. The direct
138
+ permission assignment on the objects overrides the 'allow login' ACL entry from above.
139
+
140
+ ====Overriding permissions example
141
+
142
+ ban_users = ActiveAcl::Acl.create :section => ActiveAcl::AclSection.find_by_description('users')
143
+
144
+ ban_users.allow = false
145
+ ban_users.privileges << User::LOGIN
146
+ ban_users.requesters << dr_evil
147
+
148
+ ban_users.save
149
+
150
+ john.has_privilege?(User::LOGIN) #=> true
151
+ dr_evil.has_privilege?(User::LOGIN) #=> false
152
+
153
+ ===Object level (3D) permissions
154
+ We want to assign forum permissions. We have several privileges (Forum::ADMIN, Forum::READ, Forum::POST etc.),
155
+ the afore mentioned User and UserGroup models as well as a Forum and a Category model for grouping the forums.
156
+
157
+ If we want to check if a certain user may read in a certain forum, it is not sufficient to check
158
+ <code>test_user.has_privilege?(Forum::READ)</code> as the target object - in this case a forum - is needed
159
+ to make a decision. The code to do the check is like <code>test_user.has_privilege?(Forum::READ, :on => teamforum)</code>.
160
+
161
+ To make this work you create a new ACL entry, add Forum::POST and Forum::READ as privileges, set 'allow' to
162
+ true, add the registered users group as a requester group and the public forums category as a target group
163
+ to the acl. Now every user belonging to the registered users group or a subgroup of it gains post and read
164
+ privileges on all forums of the public forums category or a subcategory of it.
165
+
166
+ ====Object level permissions example
167
+
168
+ # Assuming setup as in the above examples
169
+
170
+ class Category < ActiveRecord::Base
171
+ acts_as_nested_set
172
+ acts_as_access_group
173
+ has_many :forums
174
+ end
175
+
176
+ class Forum < ActiveRecord::Base
177
+ belongs_to :category
178
+ acts_as_access_object :grouped_by => :category
179
+ privilege_const_set 'READ' => 'read postings in forum',
180
+ 'POST' => 'reply to threads in a forum'
181
+ end
182
+
183
+ # assume there is a forum 'speakers corner' assigned to the category 'public'.
184
+
185
+ acl = ActiveAcl::Acl.create :section => ActiveAcl::AclSection.create(:description => 'forum')
186
+
187
+ acl.allow = true
188
+ acl.requester_groups << registered_users
189
+ acl.target_groups << Category.find_by_name('public')
190
+
191
+ acl.privileges << Forum::READ
192
+ acl.privileges << Forum::POST
193
+
194
+ acl.save
195
+
196
+ speakers = Forum.find_by_name('speakers corner')
197
+
198
+ john.has_privilege?(Forum::READ, :on => speakers) #=> true
199
+ john.has_privilege?(Forum::POST, :on => speakers) #=> true
200
+ anonymous.has_privilege?(Forum::READ, :on => speakers) #=> false
201
+
202
+ ==CAUTION
203
+ Do not create ACL entries that are on different branches of the inheritance hierarchy and have
204
+ allow/deny set differently on the same privilege objects. This way it's impossible to tell which
205
+ permission should take precedence. At present this is by creation date, later entries superceding older
206
+ ones, but this is most certainly not what you want.
207
+
208
+ ==Controller Actions
209
+ Defining permissions on controller actions (like "may user x execute AdminController.list ?") is quite a
210
+ common case but we are facing a problem here: Controller actions have no corresponding DB models so
211
+ permissions on them can't be easily defined.
212
+
213
+ ActiveAclPlus solves this by adding a ControllerAction and ControllerGroup model. For every public
214
+ controller method (=action) there is one ControllerAction object in the DB.
215
+
216
+ On application startup, the plugin loader checks all controller files in app/controllers and loads
217
+ or creates a ControllerAction object for every action it finds. These objects get cached in a hash
218
+ in the ActiveAclPlus module. Every controller now has a method <code>current_action</code> that looks
219
+ up and returns the access object for the current action, so it can be used for access checks like
220
+ <code>current_user.has_privilege?(ActiveAcl::ControllerAction::EXECUTE, :on => current_action)</code>.
221
+
222
+ This works nicely for before_filters with authorization checks.
223
+
224
+ A word on the load mechanism: If the action has no corresponding DB entry (it's looked up on method creation
225
+ by controller and action name) the loader searches for a controller group with the same name as the
226
+ controller. If it is found, the action is created and assigned to this group. Else the controller group
227
+ is created as a subgroup to the "unassigned controller actions" group (this name can be changed in the
228
+ options) and the unassigned action is added to the controller group.
229
+
230
+ So you are free on how to organize your controllers. Maybe create an admin group and a public group and
231
+ move controllers as a subgroup inside them?
232
+
233
+ ==Caching
234
+ The plugin provides two levels of caching. The instance cache is a hash inside the access object. The object
235
+ first tries to serve a permission request from the instance cache. If it is not found and a simple permission
236
+ is requested, the query fetches all simple permissions (2D) of the object and puts them in the instance cache. The
237
+ reason for this is that there is no noticable speed penalty in fetching all 2D permissions at once compared to
238
+ fetching only one, so this will save time and DB IO later on. Complex 3D queries are fetched independently
239
+ and also saved to the instance cache. The instance cache lives inside the access object, so it has it's
240
+ lifetime, too - which in rails usually is no more than a single request.
241
+
242
+ The second level cache tries to overcome this limitation by putting the instance cache of an access object
243
+ in an external cache. It tries to get the instance cache from there if it is not set, and sets it if it was
244
+ changed. The only real implementation for now is with the memcache daemon. The second level cache uses a timeout
245
+ (which can be defined in the options) to expire the cached permissions.
246
+
247
+ Instance and second level cache can be expired explicitly by calling <code>active_acl_clear_cache!</code>
248
+ on the access object. Calling <code>reload</code> on the object also purges the caches.
249
+
250
+ See ActiveAcl::Cache::MemcacheAdapter on how to set it up.
251
+
252
+
253
+ ==Preloader
254
+ The plugin includes a <code>load_files_from filenames</code> function. It can be used to preload source files
255
+ (and therefore the classes in it) from an application path and should be used from environment.rb.
256
+
257
+ load_files_from("#{RAILS_ROOT}/app/controllers/**/[^.]*.rb")
258
+ load_files_from("#{RAILS_ROOT}/app/models/**/[^.]*.rb")
259
+
260
+ will load all models and controllers inside these folders and their subfolders. This way you can be shure they
261
+ are registered with the ACL system at rails boot time - else they will be registered when they are called for
262
+ the first time. This means that new controllers will not show up in the admin screens until they were accessed
263
+ if not using the preloader.
264
+
265
+ ==Options
266
+ <code>ActiveAcl::OPTIONS</code> is an array that can be used to override various options for the system by setting
267
+ the values in environment.rb. <code>ActiveAcl::DEFAULT_OPTIONS</code> is as follows:
268
+
269
+ DEFAULT_OPTIONS = {
270
+ :acl_sections_table => 'acl_sections',
271
+ :acls_privileges_table => 'acls_privileges',
272
+ :acls_table => 'acls',
273
+ :privileges_table => 'privileges',
274
+ :requester_links_table => 'requester_links',
275
+ :target_links_table => 'target_links',
276
+ :requester_group_links_table => 'requester_group_links',
277
+ :target_group_links_table => 'target_group_links',
278
+ :controller_actions_table => 'controller_actions',
279
+ :controller_groups_table => 'controller_groups',
280
+
281
+ :controllers_group_name => 'unassigned_controller_actions', # the name of the base group
282
+ # that newly created controller groups get assigned to
283
+ :controller_group_name_suffix => '_controller', # name suffix for generated controller groups
284
+
285
+ :cache_permission_timeout => 10, # timeout in seconds for the second level cache
286
+
287
+ :db => ActiveAcl::DB::ActiveRecordAdapter, # the DB Adapter to use
288
+ :cache => ActiveAcl::Cache::NoCacheAdapter, # the Cache Adapter to use
289
+ }
290
+
291
+ ==Tests
292
+ Not anymore, sorry. I'm using RSpec for testing. A repository
293
+ is/will be setup at http://github.com/popel/active_acl_plus_rspec/tree/master . Check it out and move it to "spec". Run
294
+ rake spec
295
+
296
+ ==Credits
297
+ * Gregor Melhorn implemented this and maintained it up to Version 0.2.1. Thanks for releasing this!
298
+ * Evan for writing that great polymorph plugin and beeing so kind to add namespace and tablename support on Gregor's request.
299
+ * ReinH and markmeves for great support and suggestions at the rubyonrails channel on freenode.org.
300
+ * http://phpgacl.sourceforge.net as a great source of inspiration
301
+ * Obrie for writing plugin_migrations and loaded_plugins and also very nice support when Gregor got stuck with using them.
302
+
303
+ ==ToDo/Ideas
304
+
305
+ in no particular order, just a reminder...
306
+
307
+ * add materialized_tree support
308
+ * use Moneta as key/value store
309
+ * direct PostgreSQL interface
310
+ * example on how to integrate with authentication
311
+ * example on controller actions
312
+ * error checking for conflicting ACL entries
313
+ * get all permissions for a requester within a section
314
+ * get all permissions of a requester (with one query)
315
+ * get all permissions of a requester on a target (with one query)
316
+ * get all requester with a given privilege on a target (one query)
317
+ * get all targets on which a requester has a certain privilege (one query)
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 4
3
+ :patch: 4
4
+ :major: 0
@@ -0,0 +1,32 @@
1
+ module ActiveAcl
2
+
3
+
4
+ end
5
+
6
+ # plugin dependency
7
+ require 'has_many_polymorphs'
8
+
9
+ require 'active_acl/db/active_record_adapter'
10
+ require 'active_acl/cache/no_cache_adapter'
11
+ require 'active_acl/options'
12
+ require 'active_acl/base'
13
+
14
+ require 'active_acl/privilege_const_set'
15
+ require 'active_acl/grant'
16
+
17
+ require 'active_acl/handler/object_handler'
18
+ require 'active_acl/handler/nested_set'
19
+ require 'active_acl/load_controller_actions'
20
+ require 'active_acl/acts_as_access_object'
21
+ require 'active_acl/acts_as_access_group'
22
+ require 'active_acl/load_files_from'
23
+
24
+
25
+ # call class so its loaded and registered as access object
26
+ # wrap in rescue block so migrations don't fail
27
+ begin
28
+ ActiveAcl::ControllerAction
29
+ ActiveAcl::ControllerGroup
30
+ rescue StandardError => e
31
+ puts "Error #{e.message} #{e.backtrace.join("\n")}(need migrations?)"
32
+ end
@@ -0,0 +1,57 @@
1
+ require 'active_record'
2
+
3
+ module ActiveAcl #:nodoc:
4
+ module Acts #:nodoc:
5
+ module AccessGroup #:nodoc:
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ # Extend self with access group capabilites.
13
+ # Options can be:
14
+ # type:: is mandatory and is one of the group handler classes
15
+ # left_column:: for ActiveAcl::Acts::AccessGroup::NestedSet grouped objects
16
+ # right_column:: for ActiveAcl::Acts::AccessGroup::NestedSet grouped objects
17
+
18
+ def acts_as_access_group(options = {})
19
+ type=options.delete(:type) || ActiveAcl::Acts::AccessGroup::NestedSet
20
+ ActiveAcl.register_group(self,type.new(options))
21
+
22
+ include ActiveAcl::Acts::Grant
23
+ include InstanceMethods
24
+ extend SingletonMethods
25
+
26
+ ActiveAcl::Acl.instance_eval do
27
+ has_many_polymorphs :requester_groups, {:from => ActiveAcl.from_classes,
28
+ :through => :"active_acl/requester_group_links",
29
+ :rename_individual_collections => true}
30
+
31
+ has_many_polymorphs :target_groups, {:from => ActiveAcl.from_classes,
32
+ :through => :"active_acl/target_group_links",
33
+ :rename_individual_collections => true}
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+ module SingletonMethods
40
+ # class description in engine interface
41
+ def active_acl_description
42
+ name
43
+ end
44
+ end
45
+
46
+ module InstanceMethods
47
+ # override this to customize the description in the interface
48
+ def active_acl_description
49
+ to_s
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+
57
+ ActiveRecord::Base.send(:include, ActiveAcl::Acts::AccessGroup)
@@ -0,0 +1,114 @@
1
+ #require 'direct_handler'
2
+
3
+ module ActiveAcl #:nodoc:
4
+ module Acts #:nodoc:
5
+ module AccessObject #:nodoc:
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ # Extend self with access object capabilites. See README for details
14
+ # on usage. Accepts the following options as a hash:
15
+ # grouped_by:: name of the association acting as a group for access privilege
16
+ # group_class_name:: class name of group class
17
+ # join_table:: name of the join table
18
+ # foreign_key:: foreign key of self in the join table
19
+ # association_foreign_key:: foreign_key of the group class
20
+ # habtm:: set to <code>true</code> if the grup is joined with a habtm association.
21
+ # If not specified, the plugin tries to guess if the association is
22
+ # has_and_belongs_to_many or belongs_to by creating the singular form of the
23
+ # :grouped_by option and comparing it to itself: If it matches, it assumes a belongs_to association.
24
+ def acts_as_access_object(options = {})
25
+
26
+ handler=ObjectHandler.new(self,options)
27
+
28
+ ActiveAcl.register_object(self,handler)
29
+
30
+ has_many :requester_links, :as => :requester, :dependent => :delete_all, :class_name => 'ActiveAcl::RequesterLink'
31
+ has_many :requester_acls, :through => :requester_links, :source => :acl, :class_name => 'ActiveAcl::Acl'
32
+
33
+ has_many :target_links, :as => :target, :dependent => :delete_all, :class_name => 'ActiveAcl::TargetLink'
34
+ has_many :target_acls, :through => :target_links, :source => :acl, :class_name => 'ActiveAcl::Acl'
35
+
36
+ include InstanceMethods
37
+ extend SingletonMethods
38
+ include ActiveAcl::Acts::Grant
39
+
40
+ ActiveAcl::Acl.instance_eval do
41
+ has_many_polymorphs :requesters, {:from => ActiveAcl.from_classes,
42
+ :through => :"active_acl/requester_links",
43
+ :rename_individual_collections => true}
44
+
45
+ has_many_polymorphs :targets, {:from => ActiveAcl.from_classes,
46
+ :through => :"active_acl/target_links",
47
+ :rename_individual_collections => true}
48
+ end
49
+
50
+ self.module_eval do
51
+ # checks if method is defined to not break tests
52
+ unless instance_methods.include? "reload_before_active_acl"
53
+ alias :reload_before_active_acl :reload
54
+
55
+ # Redefines reload, making shure privilege caches are cleared on reload
56
+ def reload #:nodoc:
57
+ active_acl_clear_cache!
58
+ reload_before_active_acl
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+
66
+ module SingletonMethods
67
+ # class description in engine interface
68
+ def active_acl_description
69
+ return name
70
+ end
71
+ end
72
+
73
+ module InstanceMethods
74
+
75
+ # checks if the user has a certain privilege, optionally on the given object.
76
+ # Option :on defines the target object.
77
+ def has_privilege?(privilege, options = {})
78
+ target = options[:on] #TODO: add error handling if not a hash
79
+ # no need to check anything if privilege is not a Privilege
80
+ raise "first Argument has to be a Privilege" unless privilege.is_a?(Privilege)
81
+ # no need to check anything if target is no Access Object
82
+ raise "target hast to be an AccessObject (#{target.class})" if target and !(target.class.respond_to?(:base_class) && ActiveAcl.is_access_object?(target.class))
83
+
84
+ active_acl_handler.has_privilege?(self,privilege,target)
85
+ end
86
+ def active_acl_handler
87
+ ActiveAcl.object_handler(self.class)
88
+ end
89
+ #returns a key value store
90
+ def active_acl_instance_cache
91
+ @active_acl_instance_cache ||= active_acl_handler.get_instance_cache(self)
92
+ end
93
+ #returns if the 2d acls are already cached
94
+ def active_acl_cached_2d?
95
+ !!active_acl_instance_cache[:prefetched_2d]
96
+ end
97
+ def active_acl_cached_2d!
98
+ active_acl_instance_cache[:prefetched_2d]=true
99
+ end
100
+
101
+ def active_acl_clear_cache!
102
+ @active_acl_instance_cache ={} #clear the lokal cache
103
+ active_acl_handler.delete_cached(self) #clear the 2 level cache
104
+ end
105
+ # override this to customize the description in the interface
106
+ def active_acl_description
107
+ to_s
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ ActiveRecord::Base.send(:include, ActiveAcl::Acts::AccessObject)