cancannible 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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