hyper-max-mod 0.0.1

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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/cancancan-3.6.1/cancancan.gemspec +29 -0
  3. data/cancancan-3.6.1/init.rb +3 -0
  4. data/cancancan-3.6.1/lib/cancan/ability/actions.rb +93 -0
  5. data/cancancan-3.6.1/lib/cancan/ability/rules.rb +96 -0
  6. data/cancancan-3.6.1/lib/cancan/ability/strong_parameter_support.rb +41 -0
  7. data/cancancan-3.6.1/lib/cancan/ability.rb +312 -0
  8. data/cancancan-3.6.1/lib/cancan/class_matcher.rb +30 -0
  9. data/cancancan-3.6.1/lib/cancan/conditions_matcher.rb +147 -0
  10. data/cancancan-3.6.1/lib/cancan/config.rb +101 -0
  11. data/cancancan-3.6.1/lib/cancan/controller_additions.rb +399 -0
  12. data/cancancan-3.6.1/lib/cancan/controller_resource.rb +141 -0
  13. data/cancancan-3.6.1/lib/cancan/controller_resource_builder.rb +26 -0
  14. data/cancancan-3.6.1/lib/cancan/controller_resource_finder.rb +42 -0
  15. data/cancancan-3.6.1/lib/cancan/controller_resource_loader.rb +120 -0
  16. data/cancancan-3.6.1/lib/cancan/controller_resource_name_finder.rb +23 -0
  17. data/cancancan-3.6.1/lib/cancan/controller_resource_sanitizer.rb +32 -0
  18. data/cancancan-3.6.1/lib/cancan/exceptions.rb +70 -0
  19. data/cancancan-3.6.1/lib/cancan/matchers.rb +56 -0
  20. data/cancancan-3.6.1/lib/cancan/model_adapters/abstract_adapter.rb +77 -0
  21. data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_4_adapter.rb +62 -0
  22. data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_5_adapter.rb +61 -0
  23. data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_adapter.rb +229 -0
  24. data/cancancan-3.6.1/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  25. data/cancancan-3.6.1/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  26. data/cancancan-3.6.1/lib/cancan/model_adapters/default_adapter.rb +9 -0
  27. data/cancancan-3.6.1/lib/cancan/model_adapters/sti_normalizer.rb +47 -0
  28. data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/base.rb +40 -0
  29. data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb +93 -0
  30. data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb +31 -0
  31. data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/left_join.rb +11 -0
  32. data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/subquery.rb +18 -0
  33. data/cancancan-3.6.1/lib/cancan/model_additions.rb +34 -0
  34. data/cancancan-3.6.1/lib/cancan/parameter_validators.rb +9 -0
  35. data/cancancan-3.6.1/lib/cancan/relevant.rb +29 -0
  36. data/cancancan-3.6.1/lib/cancan/rule.rb +140 -0
  37. data/cancancan-3.6.1/lib/cancan/rules_compressor.rb +41 -0
  38. data/cancancan-3.6.1/lib/cancan/sti_detector.rb +12 -0
  39. data/cancancan-3.6.1/lib/cancan/unauthorized_message_resolver.rb +24 -0
  40. data/cancancan-3.6.1/lib/cancan/version.rb +5 -0
  41. data/cancancan-3.6.1/lib/cancan.rb +29 -0
  42. data/cancancan-3.6.1/lib/cancancan.rb +6 -0
  43. data/cancancan-3.6.1/lib/generators/cancan/ability/USAGE +4 -0
  44. data/cancancan-3.6.1/lib/generators/cancan/ability/ability_generator.rb +13 -0
  45. data/cancancan-3.6.1/lib/generators/cancan/ability/templates/ability.rb +32 -0
  46. data/hyper-max-mod.gemspec +12 -0
  47. metadata +86 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '031787135c4bff8ca12368b40353b03309b0832b3e12ccefed17d70e316bdece'
4
+ data.tar.gz: 789643ab46a9b7a63414958556ebd0368bfdad7e73c813d73ce4e22fca215f18
5
+ SHA512:
6
+ metadata.gz: c820c5cc41d12af0c042973aa8238885a2102fb921ba8510ce3417462024e33e90f18dc5289cbf8d9da4439859cf1c83eeb08aff7c033004cf8d79c9b5c38d74
7
+ data.tar.gz: 40d564e9b9f26b02d0914f271b18277466639ab3dbe461e9cd9669d2aa4abf58ad271f5981d19b4330da4fe7f452980ff2de093d7f9b7bf95b2c0fa86684edae
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'cancan/version'
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'cancancan'
9
+ s.version = CanCan::VERSION
10
+ s.authors = ['Alessandro Rodi (Renuo AG)', 'Bryan Rite', 'Ryan Bates', 'Richard Wilson']
11
+ s.email = 'alessandro.rodi@renuo.ch'
12
+ s.homepage = 'https://github.com/CanCanCommunity/cancancan'
13
+ s.metadata = { 'funding_uri' => 'https://github.com/sponsors/coorasse' }
14
+ s.summary = 'Simple authorization solution for Rails.'
15
+ s.description = 'Simple authorization solution for Rails. All permissions are stored in a single location.'
16
+ s.platform = Gem::Platform::RUBY
17
+ s.license = 'MIT'
18
+
19
+ s.files = `git ls-files lib init.rb cancancan.gemspec`.split($INPUT_RECORD_SEPARATOR)
20
+ s.require_paths = ['lib']
21
+
22
+ s.required_ruby_version = '>= 2.2.0'
23
+
24
+ s.add_development_dependency 'appraisal', '~> 2.0', '>= 2.0.0'
25
+ s.add_development_dependency 'bundler', '~> 2.0'
26
+ s.add_development_dependency 'rake', '~> 10.1', '>= 10.1.1'
27
+ s.add_development_dependency 'rspec', '~> 3.2', '>= 3.2.0'
28
+ s.add_development_dependency 'rubocop', '~> 1.31.1'
29
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cancan'
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module Ability
5
+ module Actions
6
+ # Alias one or more actions into another one.
7
+ #
8
+ # alias_action :update, :destroy, :to => :modify
9
+ # can :modify, Comment
10
+ #
11
+ # Then :modify permission will apply to both :update and :destroy requests.
12
+ #
13
+ # can? :update, Comment # => true
14
+ # can? :destroy, Comment # => true
15
+ #
16
+ # This only works in one direction. Passing the aliased action into the "can?" call
17
+ # will not work because aliases are meant to generate more generic actions.
18
+ #
19
+ # alias_action :update, :destroy, :to => :modify
20
+ # can :update, Comment
21
+ # can? :modify, Comment # => false
22
+ #
23
+ # Unless that exact alias is used.
24
+ #
25
+ # can :modify, Comment
26
+ # can? :modify, Comment # => true
27
+ #
28
+ # The following aliases are added by default for conveniently mapping common controller actions.
29
+ #
30
+ # alias_action :index, :show, :to => :read
31
+ # alias_action :new, :to => :create
32
+ # alias_action :edit, :to => :update
33
+ #
34
+ # This way one can use params[:action] in the controller to determine the permission.
35
+ def alias_action(*args)
36
+ target = args.pop[:to]
37
+ validate_target(target)
38
+ aliased_actions[target] ||= []
39
+ aliased_actions[target] += args
40
+ end
41
+
42
+ # Returns a hash of aliased actions. The key is the target and the value is an array of actions aliasing the key.
43
+ def aliased_actions
44
+ @aliased_actions ||= default_alias_actions
45
+ end
46
+
47
+ # Removes previously aliased actions including the defaults.
48
+ def clear_aliased_actions
49
+ @aliased_actions = {}
50
+ end
51
+
52
+ private
53
+
54
+ def default_alias_actions
55
+ {
56
+ read: %i[index show],
57
+ create: [:new],
58
+ update: [:edit]
59
+ }
60
+ end
61
+
62
+ # Given an action, it will try to find all of the actions which are aliased to it.
63
+ # This does the opposite kind of lookup as expand_actions.
64
+ def aliases_for_action(action)
65
+ results = [action]
66
+ aliased_actions.each do |aliased_action, actions|
67
+ results += aliases_for_action(aliased_action) if actions.include? action
68
+ end
69
+ results
70
+ end
71
+
72
+ def expanded_actions
73
+ @expanded_actions ||= {}
74
+ end
75
+
76
+ # Accepts an array of actions and returns an array of actions which match.
77
+ # This should be called before "matches?" and other checking methods since they
78
+ # rely on the actions to be expanded.
79
+ def expand_actions(actions)
80
+ expanded_actions[actions] ||= begin
81
+ expanded = []
82
+ actions.each do |action|
83
+ expanded << action
84
+ if (aliases = aliased_actions[action])
85
+ expanded += expand_actions(aliases)
86
+ end
87
+ end
88
+ expanded
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module Ability
5
+ module Rules
6
+ protected
7
+
8
+ # Must be protected as an ability can merge with other abilities.
9
+ # This means that an ability must expose their rules with another ability.
10
+ def rules
11
+ @rules ||= []
12
+ end
13
+
14
+ private
15
+
16
+ def add_rule(rule)
17
+ rules << rule
18
+ add_rule_to_index(rule, rules.size - 1)
19
+ end
20
+
21
+ def add_rule_to_index(rule, position)
22
+ @rules_index ||= {}
23
+
24
+ subjects = rule.subjects.compact
25
+ subjects << :all if subjects.empty?
26
+
27
+ subjects.each do |subject|
28
+ @rules_index[subject] ||= []
29
+ @rules_index[subject] << position
30
+ end
31
+ end
32
+
33
+ # Returns an array of Rule instances which match the action and subject
34
+ # This does not take into consideration any hash conditions or block statements
35
+ def relevant_rules(action, subject)
36
+ return [] unless @rules
37
+
38
+ relevant = possible_relevant_rules(subject).select do |rule|
39
+ rule.expanded_actions = expand_actions(rule.actions)
40
+ rule.relevant? action, subject
41
+ end
42
+ relevant.reverse!.uniq!
43
+ optimize_order! relevant
44
+ relevant
45
+ end
46
+
47
+ def possible_relevant_rules(subject)
48
+ if subject.is_a?(Hash)
49
+ rules
50
+ else
51
+ positions = @rules_index.values_at(subject, *alternative_subjects(subject))
52
+ positions.compact!
53
+ positions.flatten!
54
+ positions.sort!
55
+ positions.map { |i| @rules[i] }
56
+ end
57
+ end
58
+
59
+ def relevant_rules_for_match(action, subject)
60
+ relevant_rules(action, subject).each do |rule|
61
+ next unless rule.only_raw_sql?
62
+
63
+ raise Error,
64
+ "The can? and cannot? call cannot be used with a raw sql 'can' definition. " \
65
+ "The checking code cannot be determined for #{action.inspect} #{subject.inspect}"
66
+ end
67
+ end
68
+
69
+ def relevant_rules_for_query(action, subject)
70
+ rules = relevant_rules(action, subject).reject do |rule|
71
+ # reject 'cannot' rules with attributes when doing queries
72
+ rule.base_behavior == false && rule.attributes.present?
73
+ end
74
+ if rules.any?(&:only_block?)
75
+ raise Error, "The accessible_by call cannot be used with a block 'can' definition." \
76
+ "The SQL cannot be determined for #{action.inspect} #{subject.inspect}"
77
+ end
78
+ rules
79
+ end
80
+
81
+ # Optimizes the order of the rules, so that rules with the :all subject are evaluated first.
82
+ def optimize_order!(rules)
83
+ first_can_in_group = -1
84
+ rules.each_with_index do |rule, i|
85
+ (first_can_in_group = -1) && next unless rule.base_behavior
86
+ (first_can_in_group = i) && next if first_can_in_group == -1
87
+ next unless rule.subjects == [:all]
88
+
89
+ rules[i] = rules[first_can_in_group]
90
+ rules[first_can_in_group] = rule
91
+ first_can_in_group += 1
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module Ability
5
+ module StrongParameterSupport
6
+ # Returns an array of attributes suitable for use with strong parameters
7
+ #
8
+ # Note: reversing the relevant rules is important. Normal order means that 'cannot'
9
+ # rules will come before 'can' rules. However, you can't remove attributes before
10
+ # they are added. The 'reverse' is so that attributes will be added before the
11
+ # 'cannot' rules remove them.
12
+ def permitted_attributes(action, subject)
13
+ relevant_rules(action, subject)
14
+ .reverse
15
+ .select { |rule| rule.matches_conditions? action, subject }
16
+ .each_with_object(Set.new) do |rule, set|
17
+ attributes = get_attributes(rule, subject)
18
+ # add attributes for 'can', remove them for 'cannot'
19
+ rule.base_behavior ? set.merge(attributes) : set.subtract(attributes)
20
+ end.to_a
21
+ end
22
+
23
+ private
24
+
25
+ def subject_class?(subject)
26
+ klass = (subject.is_a?(Hash) ? subject.values.first : subject).class
27
+ [Class, Module].include? klass
28
+ end
29
+
30
+ def get_attributes(rule, subject)
31
+ klass = subject_class?(subject) ? subject : subject.class
32
+ # empty attributes is an 'all'
33
+ if rule.attributes.empty? && klass < ActiveRecord::Base
34
+ klass.attribute_names.map(&:to_sym) - Array(klass.primary_key)
35
+ else
36
+ rule.attributes
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,312 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'ability/rules.rb'
4
+ require_relative 'ability/actions.rb'
5
+ require_relative 'unauthorized_message_resolver.rb'
6
+ require_relative 'ability/strong_parameter_support'
7
+
8
+ module CanCan
9
+ # This module is designed to be included into an Ability class. This will
10
+ # provide the "can" methods for defining and checking abilities.
11
+ #
12
+ # class Ability
13
+ # include CanCan::Ability
14
+ #
15
+ # def initialize(user)
16
+ # if user.admin?
17
+ # can :manage, :all
18
+ # else
19
+ # can :read, :all
20
+ # end
21
+ # end
22
+ # end
23
+ #
24
+ module Ability
25
+ include CanCan::Ability::Rules
26
+ include CanCan::Ability::Actions
27
+ include CanCan::UnauthorizedMessageResolver
28
+ include StrongParameterSupport
29
+
30
+ # Check if the user has permission to perform a given action on an object.
31
+ #
32
+ # can? :destroy, @project
33
+ #
34
+ # You can also pass the class instead of an instance (if you don't have one handy).
35
+ #
36
+ # can? :create, Project
37
+ #
38
+ # Nested resources can be passed through a hash, this way conditions which are
39
+ # dependent upon the association will work when using a class.
40
+ #
41
+ # can? :create, @category => Project
42
+ #
43
+ # You can also pass multiple objects to check. You only need to pass a hash
44
+ # following the pattern { :any => [many subjects] }. The behaviour is check if
45
+ # there is a permission on any of the given objects.
46
+ #
47
+ # can? :create, {:any => [Project, Rule]}
48
+ #
49
+ #
50
+ # Any additional arguments will be passed into the "can" block definition. This
51
+ # can be used to pass more information about the user's request for example.
52
+ #
53
+ # can? :create, Project, request.remote_ip
54
+ #
55
+ # can :create, Project do |project, remote_ip|
56
+ # # ...
57
+ # end
58
+ #
59
+ # Not only can you use the can? method in the controller and view (see ControllerAdditions),
60
+ # but you can also call it directly on an ability instance.
61
+ #
62
+ # ability.can? :destroy, @project
63
+ #
64
+ # This makes testing a user's abilities very easy.
65
+ #
66
+ # def test "user can only destroy projects which he owns"
67
+ # user = User.new
68
+ # ability = Ability.new(user)
69
+ # assert ability.can?(:destroy, Project.new(:user => user))
70
+ # assert ability.cannot?(:destroy, Project.new)
71
+ # end
72
+ #
73
+ # Also see the RSpec Matchers to aid in testing.
74
+ def can?(action, subject, attribute = nil, *extra_args)
75
+ match = extract_subjects(subject).lazy.map do |a_subject|
76
+ relevant_rules_for_match(action, a_subject).detect do |rule|
77
+ rule.matches_conditions?(action, a_subject, attribute, *extra_args) && rule.matches_attributes?(attribute)
78
+ end
79
+ end.reject(&:nil?).first
80
+ match ? match.base_behavior : false
81
+ end
82
+
83
+ # Convenience method which works the same as "can?" but returns the opposite value.
84
+ #
85
+ # cannot? :destroy, @project
86
+ #
87
+ def cannot?(*args)
88
+ !can?(*args)
89
+ end
90
+
91
+ # Defines which abilities are allowed using two arguments. The first one is the action
92
+ # you're setting the permission for, the second one is the class of object you're setting it on.
93
+ #
94
+ # can :update, Article
95
+ #
96
+ # You can pass an array for either of these parameters to match any one.
97
+ # Here the user has the ability to update or destroy both articles and comments.
98
+ #
99
+ # can [:update, :destroy], [Article, Comment]
100
+ #
101
+ # You can pass :all to match any object and :manage to match any action. Here are some examples.
102
+ #
103
+ # can :manage, :all
104
+ # can :update, :all
105
+ # can :manage, Project
106
+ #
107
+ # You can pass a hash of conditions as the third argument. Here the user can only see active projects which he owns.
108
+ #
109
+ # can :read, Project, :active => true, :user_id => user.id
110
+ #
111
+ # See ActiveRecordAdditions#accessible_by for how to use this in database queries. These conditions
112
+ # are also used for initial attributes when building a record in ControllerAdditions#load_resource.
113
+ #
114
+ # If the conditions hash does not give you enough control over defining abilities, you can use a block
115
+ # along with any Ruby code you want.
116
+ #
117
+ # can :update, Project do |project|
118
+ # project.groups.include?(user.group)
119
+ # end
120
+ #
121
+ # If the block returns true then the user has that :update ability for that project, otherwise he
122
+ # will be denied access. The downside to using a block is that it cannot be used to generate
123
+ # conditions for database queries.
124
+ #
125
+ # You can pass custom objects into this "can" method, this is usually done with a symbol
126
+ # and is useful if a class isn't available to define permissions on.
127
+ #
128
+ # can :read, :stats
129
+ # can? :read, :stats # => true
130
+ #
131
+ # IMPORTANT: Neither a hash of conditions nor a block will be used when checking permission on a class.
132
+ #
133
+ # can :update, Project, :priority => 3
134
+ # can? :update, Project # => true
135
+ #
136
+ # If you pass no arguments to +can+, the action, class, and object will be passed to the block and the
137
+ # block will always be executed. This allows you to override the full behavior if the permissions are
138
+ # defined in an external source such as the database.
139
+ #
140
+ # can do |action, object_class, object|
141
+ # # check the database and return true/false
142
+ # end
143
+ #
144
+ def can(action = nil, subject = nil, *attributes_and_conditions, &block)
145
+ add_rule(Rule.new(true, action, subject, *attributes_and_conditions, &block))
146
+ end
147
+
148
+ # Defines an ability which cannot be done. Accepts the same arguments as "can".
149
+ #
150
+ # can :read, :all
151
+ # cannot :read, Comment
152
+ #
153
+ # A block can be passed just like "can", however if the logic is complex it is recommended
154
+ # to use the "can" method.
155
+ #
156
+ # cannot :read, Product do |product|
157
+ # product.invisible?
158
+ # end
159
+ #
160
+ def cannot(action = nil, subject = nil, *attributes_and_conditions, &block)
161
+ add_rule(Rule.new(false, action, subject, *attributes_and_conditions, &block))
162
+ end
163
+
164
+ # User shouldn't specify targets with names of real actions or it will cause Seg fault
165
+ def validate_target(target)
166
+ error_message = "You can't specify target (#{target}) as alias because it is real action name"
167
+ raise Error, error_message if aliased_actions.values.flatten.include? target
168
+ end
169
+
170
+ def model_adapter(model_class, action)
171
+ adapter_class = ModelAdapters::AbstractAdapter.adapter_class(model_class)
172
+ adapter_class.new(model_class, relevant_rules_for_query(action, model_class))
173
+ end
174
+
175
+ # See ControllerAdditions#authorize! for documentation.
176
+ def authorize!(action, subject, *args)
177
+ message = args.last.is_a?(Hash) && args.last.key?(:message) ? args.pop[:message] : nil
178
+ if cannot?(action, subject, *args)
179
+ message ||= unauthorized_message(action, subject)
180
+ raise AccessDenied.new(message, action, subject, args)
181
+ end
182
+ subject
183
+ end
184
+
185
+ def attributes_for(action, subject)
186
+ attributes = {}
187
+ relevant_rules(action, subject).map do |rule|
188
+ attributes.merge!(rule.attributes_from_conditions) if rule.base_behavior
189
+ end
190
+ attributes
191
+ end
192
+
193
+ def has_block?(action, subject)
194
+ relevant_rules(action, subject).any?(&:only_block?)
195
+ end
196
+
197
+ def has_raw_sql?(action, subject)
198
+ relevant_rules(action, subject).any?(&:only_raw_sql?)
199
+ end
200
+
201
+ # Copies all rules and aliased actions of the given +CanCan::Ability+ and adds them to +self+.
202
+ # class ReadAbility
203
+ # include CanCan::Ability
204
+ #
205
+ # def initialize
206
+ # can :read, User
207
+ # alias_action :show, :index, to: :see
208
+ # end
209
+ # end
210
+ #
211
+ # class WritingAbility
212
+ # include CanCan::Ability
213
+ #
214
+ # def initialize
215
+ # can :edit, User
216
+ # alias_action :create, :update, to: :modify
217
+ # end
218
+ # end
219
+ #
220
+ # read_ability = ReadAbility.new
221
+ # read_ability.can? :edit, User.new #=> false
222
+ # read_ability.merge(WritingAbility.new)
223
+ # read_ability.can? :edit, User.new #=> true
224
+ # read_ability.aliased_actions #=> [:see => [:show, :index], :modify => [:create, :update]]
225
+ #
226
+ # If there are collisions when merging the +aliased_actions+, the actions on +self+ will be
227
+ # overwritten.
228
+ #
229
+ # class ReadAbility
230
+ # include CanCan::Ability
231
+ #
232
+ # def initialize
233
+ # alias_action :show, :index, to: :see
234
+ # end
235
+ # end
236
+ #
237
+ # class ShowAbility
238
+ # include CanCan::Ability
239
+ #
240
+ # def initialize
241
+ # alias_action :show, to: :see
242
+ # end
243
+ # end
244
+ #
245
+ # read_ability = ReadAbility.new
246
+ # read_ability.merge(ShowAbility)
247
+ # read_ability.aliased_actions #=> [:see => [:show]]
248
+ def merge(ability)
249
+ ability.rules.each do |rule|
250
+ add_rule(rule.dup)
251
+ end
252
+ @aliased_actions = aliased_actions.merge(ability.aliased_actions)
253
+ self
254
+ end
255
+
256
+ # Return a hash of permissions for the user in the format of:
257
+ # {
258
+ # can: can_hash,
259
+ # cannot: cannot_hash
260
+ # }
261
+ #
262
+ # Where can_hash and cannot_hash are formatted thusly:
263
+ # {
264
+ # action: { subject: [attributes] }
265
+ # }
266
+ def permissions
267
+ permissions_list = {
268
+ can: Hash.new { |actions, k1| actions[k1] = Hash.new { |subjects, k2| subjects[k2] = [] } },
269
+ cannot: Hash.new { |actions, k1| actions[k1] = Hash.new { |subjects, k2| subjects[k2] = [] } }
270
+ }
271
+ rules.each { |rule| extract_rule_in_permissions(permissions_list, rule) }
272
+ permissions_list
273
+ end
274
+
275
+ def extract_rule_in_permissions(permissions_list, rule)
276
+ expand_actions(rule.actions).each do |action|
277
+ container = rule.base_behavior ? :can : :cannot
278
+ rule.subjects.each do |subject|
279
+ permissions_list[container][action][subject.to_s] += rule.attributes
280
+ end
281
+ end
282
+ end
283
+
284
+ private
285
+
286
+ def unauthorized_message_keys(action, subject)
287
+ subject = (subject.class == Class ? subject : subject.class).name.underscore unless subject.is_a? Symbol
288
+ aliases = aliases_for_action(action)
289
+ [subject, :all].product([*aliases, :manage]).map do |try_subject, try_action|
290
+ :"#{try_action}.#{try_subject}"
291
+ end
292
+ end
293
+
294
+ # It translates to an array the subject or the hash with multiple subjects given to can?.
295
+ def extract_subjects(subject)
296
+ if subject.is_a?(Hash) && subject.key?(:any)
297
+ subject[:any]
298
+ else
299
+ [subject]
300
+ end
301
+ end
302
+
303
+ def alternative_subjects(subject)
304
+ subject = subject.class unless subject.is_a?(Module)
305
+ if subject.respond_to?(:subclasses) && defined?(ActiveRecord::Base) && subject < ActiveRecord::Base
306
+ [:all, *(subject.ancestors + subject.subclasses), subject.class.to_s]
307
+ else
308
+ [:all, *subject.ancestors, subject.class.to_s]
309
+ end
310
+ end
311
+ end
312
+ end
@@ -0,0 +1,30 @@
1
+ require_relative 'sti_detector'
2
+
3
+ # This class is responsible for matching classes and their subclasses as well as
4
+ # upmatching classes to their ancestors.
5
+ # This is used to generate sti connections
6
+ class SubjectClassMatcher
7
+ def self.matches_subject_class?(subjects, subject)
8
+ subjects.any? do |sub|
9
+ has_subclasses = subject.respond_to?(:subclasses)
10
+ matching_class_check(subject, sub, has_subclasses)
11
+ end
12
+ end
13
+
14
+ def self.matching_class_check(subject, sub, has_subclasses)
15
+ matches = matches_class_or_is_related(subject, sub)
16
+ if has_subclasses
17
+ return matches unless StiDetector.sti_class?(sub)
18
+
19
+ matches || subject.subclasses.include?(sub)
20
+ else
21
+ matches
22
+ end
23
+ end
24
+
25
+ def self.matches_class_or_is_related(subject, sub)
26
+ sub.is_a?(Module) && (subject.is_a?(sub) ||
27
+ subject.class.to_s == sub.to_s ||
28
+ (subject.is_a?(Module) && subject.ancestors.include?(sub)))
29
+ end
30
+ end