cancannible 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YTJjNWE0NzMzZTcyNDAxMDRhOTMxZmE4ODRkYWZkMWU1ZDcwNjczMw==
4
+ YWE0OWZkMjhlMWQ3ODMzNTVmZWNiMTk2ZWM5YmU2YjMyY2I1NTAwMA==
5
5
  data.tar.gz: !binary |-
6
- NGY4MDliNGJlMGMwZjBkYTc5MjYyYzcyNWI0MWZiNWJmMmU1ZjU3Mw==
6
+ YjQ0NDZhNzQ5MzdhMDJiMDQ4NzRjMjI1MDE5ZGY1N2ViZjU2NThlMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MTliYzQ2MGYzZWQyODhiMTk0ZTgzODhjNDQ1MDEwMzdmZDNjOWY0ZjRiY2Q0
10
- NDNjZGUwYmE4OTQ3Zjc2MjZkMDc1N2JjOTYyOWE2ZGY4OWZhNGE5NTkyYjg4
11
- ODFjZTYyODc0Yjg5ZGM4YWRmMjI1ZmU4ZmFmZGU5NmExMzhjZTE=
9
+ Zjc2OGYzMGI1ODA5ZmY4ZmQ3ZGRhNTU5MDM2MmRiOWJmNDNmNzI4MzZjYTY0
10
+ ZDNiNDM3ODVmZTExYzM5NDQ1ZmQyNDZmZjc0NDFiYWIxZTM2YjcwMTdiMGU0
11
+ YTliZGI0YjFhNDdjY2RmZWNhZjNhY2YyNDU5YTQ3MDQ3NDRhNjY=
12
12
  data.tar.gz: !binary |-
13
- NmM5ODQ3NmUwNDIzYjJkYTZjZTZlYzhiNDEzMTAxYWQzMmE0ODU1OGJiNTFm
14
- OWRjMzUyZWUzMmQxYTI1ZmQ2MGIzMjZjOWRkNTI0N2VlZjkyN2NjNjljM2Ez
15
- ZmY5M2RkMzgyYTk1OWE5OGFkYzUwNTE1ZjRlNmQyNzVkYjkxNWE=
13
+ MjFhMzBmNzQ5YTcyMzBmOTViMTYwMjEwNDI2YjllNjUxZjEzYjkxNmZjOTVi
14
+ YmVkOWZjMWUyODBiMDQ3MmY4YWExMDJiNmUzZTkzZTBlY2MwZWFjMzFmOWM0
15
+ MjNiMTBmMGFjNGI3OTllNTkxMjM3NTBmZDViOWYwNmVlZGMwZWE=
data/README.md CHANGED
@@ -34,6 +34,7 @@ Or install it yourself as:
34
34
 
35
35
  $ gem install cancannible
36
36
 
37
+
37
38
  ## Configuration
38
39
 
39
40
  A generator is provided to create:
@@ -44,13 +45,56 @@ After installing the gem, run the generator:
44
45
 
45
46
  $ rails generate cancannible:install
46
47
 
48
+
49
+ ## Enable Cancannible support in a model
50
+
51
+ Include Cancannible::Grantee in each model that it will be valid to assign permissions to.
52
+
53
+ For example, if we have a User model associated with a Group, and both can have permissions assigned:
54
+
55
+ class User < ActiveRecord::Base
56
+ belongs_to :group
57
+ include Cancannible::Grantee
58
+ end
59
+
60
+ class Group < ActiveRecord::Base
61
+ has_many :users
62
+ include Cancannible::Grantee
63
+ end
64
+
65
+
66
+ ## Enabling Permissions inheritance
67
+
68
+ By default, permissions are not inherited from association.
69
+ User the `inherit_permissions_from` class method to declare how permissions can be inherited.
70
+
71
+ For example:
72
+
73
+ class User < ActiveRecord::Base
74
+ belongs_to :group
75
+ include Cancannible::Grantee
76
+ inherit_permissions_from :group
77
+ end
78
+
79
+ Or:
80
+
81
+ class User < ActiveRecord::Base
82
+ belongs_to :group
83
+ has_many :roles_users, class_name: 'RolesUsers'
84
+ has_many :roles, through: :roles_users
85
+ include Cancannible::Grantee
86
+ inherit_permissions_from :group, :roles
87
+ end
88
+
89
+
47
90
  ## The Cancannible initialization file
48
91
 
49
92
  See the initialization file template for specific instructions. Use the initialization file to configure:
50
93
  * abilities caching
51
94
  * general-purpose access refinements
52
95
 
53
- ## Configuring cached abilities storage
96
+
97
+ ### Configuring cached abilities storage
54
98
 
55
99
  Cancannible does not implement any specific storage mechanism - that is up to you to provide if you wish.
56
100
 
@@ -4,5 +4,6 @@ require 'cancan'
4
4
 
5
5
  require "cancannible/version"
6
6
  require "cancannible/config"
7
- require "cancannible/ability_preload_adapter"
8
- require "cancannible/base"
7
+ require "cancannible/preload_adapter"
8
+ require "cancannible/preloader"
9
+ require "cancannible/grantee"
@@ -20,7 +20,9 @@ module Cancannible
20
20
  reset!
21
21
 
22
22
  def self.refine_access(refinement={})
23
- self.refinements << refinement
23
+ stage = (refinement.delete(:stage) || 1) - 1
24
+ self.refinements[stage] ||= []
25
+ self.refinements[stage] << refinement
24
26
  end
25
27
 
26
28
  end
@@ -0,0 +1,125 @@
1
+ module Cancannible::Grantee
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ class_attribute :inheritable_permissions
6
+ self.inheritable_permissions = [] # default
7
+
8
+ has_many :permissions, as: :permissible, dependent: :destroy do
9
+ # generally use instance.can method, not permissions<< directly
10
+ def <<(arg)
11
+ ability, resource = arg
12
+ resource = nil if resource.blank?
13
+ asserted = arg[2].nil? ? true : arg[2]
14
+
15
+ case resource
16
+ when Class, Symbol
17
+ resource_type = resource.to_s
18
+ resource_id = nil
19
+ when nil
20
+ resource_type = resource_id = nil
21
+ else
22
+ resource_type = resource.class.to_s
23
+ resource_id = resource.try(:id)
24
+ end
25
+
26
+ permission = find_by_asserted_and_ability_and_resource_id_and_resource_type(
27
+ asserted, ability, resource_id, resource_type)
28
+ unless permission
29
+ permission = find_or_initialize_by_asserted_and_ability_and_resource_id_and_resource_type(
30
+ !asserted, ability, resource_id, resource_type)
31
+ permission.asserted = asserted
32
+ permission.save!
33
+ end
34
+
35
+ # if Rails.version =~ /3\.0/ # the rails 3.0 way
36
+ # proxy_owner.instance_variable_set :@permissions, nil # invalidate the owner's permissions collection
37
+ # proxy_owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
38
+ # else
39
+ proxy_association.owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
40
+ # end
41
+ permission
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ module ClassMethods
48
+
49
+ # Command: configures the set of associations (array of symbols) from which permissions should be inherited
50
+ def inherit_permissions_from(*relations)
51
+ self.inheritable_permissions = relations
52
+ end
53
+
54
+ end
55
+
56
+ # Returns the Ability set for the owner.
57
+ # Set +refresh+ to true to force a reload of permissions.
58
+ def abilities(refresh = false)
59
+ @abilities = if refresh
60
+ nil
61
+ elsif Cancannible.get_cached_abilities.respond_to?(:call)
62
+ Cancannible.get_cached_abilities.call(self)
63
+ end
64
+ return @abilities if @abilities
65
+
66
+ @abilities ||= if ability_class = ('Ability'.constantize rescue nil)
67
+ unless ability_class.included_modules.include?(Cancannible::PreloadAdapter)
68
+ ability_class.send :include, Cancannible::PreloadAdapter
69
+ end
70
+ ability_class.new(self)
71
+ end
72
+
73
+ Cancannible.store_cached_abilities.call(self,@abilities) if Cancannible.store_cached_abilities.respond_to?(:call)
74
+ @abilities
75
+ end
76
+
77
+ # Returns the collection of inherited permission records
78
+ def inherited_permissions
79
+ inherited_perms = []
80
+ self.class.inheritable_permissions.each do |relation|
81
+ Array(self.send(relation)).each do |record|
82
+ inherited_perms.concat(record.permissions.reload)
83
+ end
84
+ end
85
+ inherited_perms
86
+ end
87
+
88
+ # Returns true it the +ability+ is permitted on +resource+ - persisted or dynamic (delegated to CanCan)
89
+ def can?(ability, resource)
90
+ abilities.can?(ability, resource)
91
+ end
92
+
93
+ # Returns true it the +ability+ is prohibited on +resource+ - persisted or dynamic (delegated to CanCan)
94
+ def cannot?(ability, resource)
95
+ abilities.cannot?(ability, resource)
96
+ end
97
+
98
+ # Command: grant the permission to do +ability+ on +resource+
99
+ def can(ability, resource)
100
+ permissions << [ability, resource]
101
+ end
102
+
103
+ # Command: prohibit the permission to do +ability+ on +resource+
104
+ def cannot(ability, resource)
105
+ permissions << [ability, resource, false]
106
+ end
107
+
108
+ end
109
+
110
+
111
+ module Cancannible
112
+ # This module is automatically included into all controllers.
113
+ # It overrides some CanCan ControllerAdditions
114
+ module ControllerAdditions
115
+ def current_ability
116
+ current_user.try(:abilities)
117
+ end
118
+ end
119
+ end
120
+
121
+ if defined? ActionController::Base
122
+ ActionController::Base.class_eval do
123
+ include Cancannible::ControllerAdditions
124
+ end
125
+ end
@@ -1,4 +1,4 @@
1
- module Cancannible::AbilityPreloadAdapter
1
+ module Cancannible::PreloadAdapter
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
@@ -6,7 +6,7 @@ module Cancannible::AbilityPreloadAdapter
6
6
  # Tap Ability.new to first preload permissions via Cancannible
7
7
  alias_method :cancan_initialize, :initialize
8
8
  def initialize(user)
9
- user.preload_abilities(self) if user.respond_to? :preload_abilities
9
+ Cancannible::Preloader.preload_abilities!(user,self)
10
10
  cancan_initialize(user)
11
11
  end
12
12
 
@@ -0,0 +1,115 @@
1
+ class Cancannible::Preloader
2
+
3
+ def self.preload_abilities!(grantee,cancan_ability_object)
4
+ new(grantee,cancan_ability_object).preload!
5
+ end
6
+
7
+ attr_accessor :grantee
8
+ attr_accessor :cancan_ability_object
9
+
10
+ def initialize(grantee,cancan_ability_object)
11
+ self.grantee = grantee
12
+ self.cancan_ability_object = cancan_ability_object
13
+ end
14
+
15
+ def preload!
16
+ return unless grantee.respond_to?(:inherited_permissions)
17
+ # load inherited permissions to CanCan Abilities
18
+ preload_abilities_from_permissions(grantee.inherited_permissions)
19
+ # load user-based permissions from database to CanCan Abilities
20
+ preload_abilities_from_permissions(grantee.permissions.reload)
21
+ # return the ability object
22
+ cancan_ability_object
23
+ end
24
+
25
+ def preload_abilities_from_permissions(perms)
26
+ perms.each do |permission|
27
+ ability = permission.ability.to_sym
28
+ action = permission.asserted ? :can : :cannot
29
+
30
+ resource_type,model_resource = resolve_resource_type(permission.resource_type)
31
+
32
+ if !resource_type || resource_type.is_a?(Symbol)
33
+ # nil or symbolic resource types: apply generic unrestricted permission to the resource_type
34
+ cancan_ability_object.send( action, ability, resource_type )
35
+ next
36
+ else
37
+ # model-based resource types: skip if we cannot get a model instance
38
+ next unless model_resource
39
+ end
40
+
41
+ if permission.resource_id.nil?
42
+
43
+ if action == :cannot
44
+ # apply generic unrestricted permission to the class
45
+ cancan_ability_object.send( action, ability, resource_type )
46
+ else
47
+
48
+ refinements = resolve_resource_refinements(ability,model_resource)
49
+
50
+ if refinements.empty?
51
+ # apply generic unrestricted permission to the class
52
+ cancan_ability_object.send( action, ability, resource_type )
53
+ else
54
+ secondary_refinements = resolve_resource_refinements(ability,model_resource,2).presence || [{}]
55
+ refinements.each do |refinement|
56
+ secondary_refinements.each do |secondary_refinement|
57
+ cancan_ability_object.send( action, ability, resource_type, refinement.merge(secondary_refinement))
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ elsif resource_type.find_by_id(permission.resource_id)
65
+ cancan_ability_object.send( action, ability, resource_type, id: permission.resource_id)
66
+ end
67
+
68
+ end
69
+ end
70
+
71
+ def resolve_resource_type(given_resource_type)
72
+ model_resource = nil
73
+ resource_type = given_resource_type
74
+ resource_type = resource_type==resource_type.downcase ? resource_type.to_sym : resource_type.constantize rescue nil
75
+ model_resource = resource_type.respond_to?(:new) ? resource_type.new : resource_type rescue nil
76
+ [resource_type,model_resource]
77
+ end
78
+
79
+ def resolve_resource_refinements(ability,model_resource,stage=1)
80
+ Array(Cancannible.refinements[stage-1]).each_with_object([]) do |refinement,memo|
81
+ refinement_attributes = refinement.dup
82
+
83
+ allow_nil = !!(refinement_attributes.delete(:allow_nil))
84
+
85
+ refinement_if_condition = refinement_attributes.delete(:if)
86
+ next if refinement_if_condition.respond_to?(:call) && !refinement_if_condition.call(grantee,model_resource)
87
+
88
+ refinement_scope = Array(refinement_attributes.delete(:scope))
89
+ next if refinement_scope.present? && !refinement_scope.include?(ability)
90
+
91
+ refinement_except = Array(refinement_attributes.delete(:except))
92
+ next if refinement_except.present? && refinement_except.include?(ability)
93
+
94
+ refinement_attribute_names = refinement_attributes.keys.map{|k| "#{k}" }
95
+ next unless (refinement_attribute_names - model_resource.attribute_names).empty?
96
+
97
+ restriction = {}
98
+ refinement_attributes.each do |key,value|
99
+ if value.is_a?(Symbol)
100
+ if grantee.respond_to?(value)
101
+ restriction[key] = if allow_nil
102
+ Array(grantee.send(value)) + [nil]
103
+ else
104
+ grantee.send(value)
105
+ end
106
+ end
107
+ else
108
+ restriction[key] = value
109
+ end
110
+ end
111
+ memo.push(restriction) if restriction.present?
112
+ end
113
+ end
114
+
115
+ end
@@ -1,3 +1,3 @@
1
1
  module Cancannible
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -94,4 +94,10 @@ Cancannible.setup do |config|
94
94
  # and `model_resource` is an example record (unsaved) of the kind of resource that the rule is being applied to.
95
95
 
96
96
 
97
+ # Multi-stage refinement syntax example:
98
+ # config.refine_access customer_id: :accessible_customer_ids, stage: 2
99
+ #
100
+ # By default, access refinements are "stage 1" i.e. applied directly to the permissions being loaded.
101
+ # By specifying stage 2, this refinement is applied on top of all stage 1 refinements (if possible / applicable)
102
+
97
103
  end
@@ -11,15 +11,15 @@ class Permission < ActiveRecord::Base
11
11
  end
12
12
 
13
13
  class Member < ActiveRecord::Base
14
- include Cancannible
14
+ include Cancannible::Grantee
15
15
  end
16
16
 
17
17
  class User < ActiveRecord::Base
18
18
  has_many :roles_users, class_name: 'RolesUsers'
19
- has_many :roles, :through => :roles_users
19
+ has_many :roles, through: :roles_users
20
20
  belongs_to :group
21
21
 
22
- include Cancannible
22
+ include Cancannible::Grantee
23
23
  inherit_permissions_from :roles, :group
24
24
  end
25
25
 
@@ -30,15 +30,15 @@ end
30
30
 
31
31
  class Role < ActiveRecord::Base
32
32
  has_many :roles_users, :class_name => 'RolesUsers'
33
- has_many :users, :through => :roles_users
33
+ has_many :users, through: :roles_users
34
34
 
35
- include Cancannible
35
+ include Cancannible::Grantee
36
36
  end
37
37
 
38
38
  class Group < ActiveRecord::Base
39
39
  has_many :users
40
40
 
41
- include Cancannible
41
+ include Cancannible::Grantee
42
42
  end
43
43
 
44
44
  class Widget < ActiveRecord::Base
@@ -217,6 +217,34 @@ describe Cancannible do
217
217
  end
218
218
  end
219
219
 
220
+ context "with stage 2 restriction" do
221
+ let(:resource_class) { Widget }
222
+ before do
223
+ Cancannible.setup do |config|
224
+ config.refine_access category_id: :category_ids
225
+ config.refine_access name: 'Test', stage: 2
226
+ end
227
+ allow(grantee).to receive(:category_ids).and_return([1,3])
228
+ grantee.can(ability,resource_class)
229
+ end
230
+ let!(:resource) { resource_class.create(category_id: category_id, name: name) }
231
+ subject { grantee.can?(ability,resource) }
232
+ context "with resource within scope" do
233
+ let(:name) { 'Test' }
234
+ let(:category_id) { 3 }
235
+ it { should be_truthy }
236
+ end
237
+ context "with resource not in scope (excluded by attribute association)" do
238
+ let(:name) { 'Test' }
239
+ let(:category_id) { 2 }
240
+ it { should be_falsey }
241
+ end
242
+ context "with resource not in scope (excluded by stage 2 refinement)" do
243
+ let(:name) { 'Not Test' }
244
+ let(:category_id) { 3 }
245
+ it { should be_falsey }
246
+ end
247
+ end
220
248
 
221
249
  end
222
250
 
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Cancannible do
3
+ describe Cancannible::Grantee do
4
4
  let(:grantee_class) { Member }
5
5
 
6
6
  context "without permissions inheritance" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cancannible
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paul Gallagher
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-29 00:00:00.000000000 Z
11
+ date: 2014-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -154,9 +154,10 @@ files:
154
154
  - Rakefile
155
155
  - cancannible.gemspec
156
156
  - lib/cancannible.rb
157
- - lib/cancannible/ability_preload_adapter.rb
158
- - lib/cancannible/base.rb
159
157
  - lib/cancannible/config.rb
158
+ - lib/cancannible/grantee.rb
159
+ - lib/cancannible/preload_adapter.rb
160
+ - lib/cancannible/preloader.rb
160
161
  - lib/cancannible/version.rb
161
162
  - lib/generators/cancannible/install_generator.rb
162
163
  - lib/generators/cancannible/templates/cancannible_initializer.rb
@@ -166,10 +167,10 @@ files:
166
167
  - spec/support/ability.rb
167
168
  - spec/support/migrations_helper.rb
168
169
  - spec/support/models.rb
169
- - spec/unit/base_spec.rb
170
170
  - spec/unit/cached_abilities_spec.rb
171
171
  - spec/unit/config_spec.rb
172
172
  - spec/unit/custom_refinements_spec.rb
173
+ - spec/unit/grantee_spec.rb
173
174
  - spec/unit/inherited_permissions_spec.rb
174
175
  homepage: https://github.com/evendis/cancannible
175
176
  licenses:
@@ -200,8 +201,8 @@ test_files:
200
201
  - spec/support/ability.rb
201
202
  - spec/support/migrations_helper.rb
202
203
  - spec/support/models.rb
203
- - spec/unit/base_spec.rb
204
204
  - spec/unit/cached_abilities_spec.rb
205
205
  - spec/unit/config_spec.rb
206
206
  - spec/unit/custom_refinements_spec.rb
207
+ - spec/unit/grantee_spec.rb
207
208
  - spec/unit/inherited_permissions_spec.rb
@@ -1,213 +0,0 @@
1
- module Cancannible
2
- extend ActiveSupport::Concern
3
-
4
- included do
5
- class_attribute :inheritable_permissions
6
- self.inheritable_permissions = [] # default
7
-
8
- has_many :permissions, as: :permissible, dependent: :destroy do
9
- # generally use instance.can method, not permissions<< directly
10
- def <<(arg)
11
- ability, resource = arg
12
- resource = nil if resource.blank?
13
- asserted = arg[2].nil? ? true : arg[2]
14
-
15
- case resource
16
- when Class, Symbol
17
- resource_type = resource.to_s
18
- resource_id = nil
19
- when nil
20
- resource_type = resource_id = nil
21
- else
22
- resource_type = resource.class.to_s
23
- resource_id = resource.try(:id)
24
- end
25
-
26
- permission = find_by_asserted_and_ability_and_resource_id_and_resource_type(
27
- asserted, ability, resource_id, resource_type)
28
- unless permission
29
- permission = find_or_initialize_by_asserted_and_ability_and_resource_id_and_resource_type(
30
- !asserted, ability, resource_id, resource_type)
31
- permission.asserted = asserted
32
- permission.save!
33
- end
34
-
35
- # if Rails.version =~ /3\.0/ # the rails 3.0 way
36
- # proxy_owner.instance_variable_set :@permissions, nil # invalidate the owner's permissions collection
37
- # proxy_owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
38
- # else
39
- proxy_association.owner.instance_variable_set :@abilities, nil # invalidate the owner's ability collection
40
- # end
41
- permission
42
- end
43
- end
44
-
45
- end
46
-
47
- module ClassMethods
48
- def inherit_permissions_from(*relations)
49
- self.inheritable_permissions = relations
50
- end
51
- end
52
-
53
- # Returns the Ability set for the owner.
54
- # Set +refresh+ to true to force a reload of permissions.
55
- def abilities(refresh = false)
56
- @abilities = if refresh
57
- nil
58
- elsif get_cached_abilities.respond_to?(:call)
59
- get_cached_abilities.call(self)
60
- end
61
- return @abilities if @abilities
62
-
63
- @abilities ||= if ability_class = ('Ability'.constantize rescue nil)
64
- unless ability_class.included_modules.include?(Cancannible::AbilityPreloadAdapter)
65
- ability_class.send :include, Cancannible::AbilityPreloadAdapter
66
- end
67
- ability_class.new(self)
68
- end
69
-
70
- store_cached_abilities.call(self,@abilities) if store_cached_abilities.respond_to?(:call)
71
- @abilities
72
- end
73
-
74
- def preload_abilities(cancan_ability_object)
75
- # load inherited permissions to CanCan Abilities
76
- preload_abilities_from_permissions(cancan_ability_object, inherited_permissions)
77
- # load user-based permissions from database to CanCan Abilities
78
- preload_abilities_from_permissions(cancan_ability_object, self.permissions.reload)
79
- cancan_ability_object
80
- end
81
-
82
- def inherited_permissions
83
- inherited_perms = []
84
- self.class.inheritable_permissions.each do |relation|
85
- Array(self.send(relation)).each do |record|
86
- inherited_perms.concat(record.permissions.reload)
87
- end
88
- end
89
- inherited_perms
90
- end
91
-
92
- # test for a permission - persisted or dynamic (delegated to CanCan)
93
- def can?(ability, resource)
94
- abilities.can?(ability, resource)
95
- end
96
-
97
- # test for a prohibition - persisted or dynamic (delegated to CanCan)
98
- def cannot?(ability, resource)
99
- abilities.cannot?(ability, resource)
100
- end
101
-
102
- # define a persisted permission
103
- def can(ability, resource)
104
- permissions << [ability, resource]
105
- end
106
-
107
- # define a persisted prohibition
108
- def cannot(ability, resource)
109
- permissions << [ability, resource, false]
110
- end
111
-
112
- private
113
-
114
- def preload_abilities_from_permissions(cancan_ability_object,perms)
115
- perms.each do |permission|
116
- ability = permission.ability.to_sym
117
- action = permission.asserted ? :can : :cannot
118
-
119
- if resource_type = permission.resource_type
120
- begin
121
- resource_type = resource_type==resource_type.downcase ? resource_type.to_sym : resource_type.constantize
122
- model_resource = resource_type.respond_to?(:new) ? resource_type.new : resource_type
123
- rescue
124
- model_resource = nil
125
- end
126
- end
127
-
128
- if !resource_type || resource_type.is_a?(Symbol)
129
- # nil or symbolic resource types:
130
- # apply generic unrestricted permission to the resource_type
131
- cancan_ability_object.send( action, ability, resource_type )
132
- next
133
- else
134
- # model-based resource types:
135
- # skip if we cannot get a model instance
136
- next unless model_resource
137
- end
138
-
139
- if permission.resource_id.nil?
140
-
141
- if action == :cannot
142
- # apply generic unrestricted permission to the class
143
- cancan_ability_object.send( action, ability, resource_type )
144
- else
145
-
146
- refinements = Cancannible.refinements.each_with_object([]) do |refinement,memo|
147
- refinement_attributes = refinement.dup
148
-
149
- allow_nil = !!(refinement_attributes.delete(:allow_nil))
150
-
151
- refinement_if_condition = refinement_attributes.delete(:if)
152
- next if refinement_if_condition.respond_to?(:call) && !refinement_if_condition.call(self,model_resource)
153
-
154
- refinement_scope = Array(refinement_attributes.delete(:scope))
155
- next if refinement_scope.present? && !refinement_scope.include?(ability)
156
-
157
- refinement_except = Array(refinement_attributes.delete(:except))
158
- next if refinement_except.present? && refinement_except.include?(ability)
159
-
160
- refinement_attribute_names = refinement_attributes.keys.map{|k| "#{k}" }
161
- next unless (refinement_attribute_names - model_resource.attribute_names).empty?
162
-
163
- restriction = {}
164
- refinement_attributes.each do |key,value|
165
- if value.is_a?(Symbol)
166
- if self.respond_to?(value)
167
- restriction[key] = if allow_nil
168
- Array(self.send(value)) + [nil]
169
- else
170
- self.send(value)
171
- end
172
- end
173
- else
174
- restriction[key] = value
175
- end
176
- end
177
- memo.push(restriction) if restriction.present?
178
- end
179
-
180
- if refinements.empty?
181
- # apply generic unrestricted permission to the class
182
- cancan_ability_object.send( action, ability, resource_type )
183
- else
184
- refinements.each do |refinement|
185
- cancan_ability_object.send( action, ability, resource_type, refinement)
186
- end
187
- end
188
-
189
- end
190
-
191
- elsif resource_type.find_by_id(permission.resource_id)
192
- cancan_ability_object.send( action, ability, resource_type, id: permission.resource_id)
193
- end
194
- end
195
- end
196
- end
197
-
198
-
199
- module Cancannible
200
- # This module is automatically included into all controllers.
201
- # It overrides some CanCan ControllerAdditions
202
- module ControllerAdditions
203
- def current_ability
204
- current_user.try(:abilities)
205
- end
206
- end
207
- end
208
-
209
- if defined? ActionController::Base
210
- ActionController::Base.class_eval do
211
- include Cancannible::ControllerAdditions
212
- end
213
- end