cancancan 1.17.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +5 -5
  2. data/cancancan.gemspec +10 -11
  3. data/init.rb +2 -0
  4. data/lib/cancan/ability/actions.rb +93 -0
  5. data/lib/cancan/ability/rules.rb +96 -0
  6. data/lib/cancan/ability/strong_parameter_support.rb +41 -0
  7. data/lib/cancan/ability.rb +87 -198
  8. data/lib/cancan/class_matcher.rb +30 -0
  9. data/lib/cancan/conditions_matcher.rb +147 -0
  10. data/lib/cancan/config.rb +101 -0
  11. data/lib/cancan/controller_additions.rb +13 -30
  12. data/lib/cancan/controller_resource.rb +33 -225
  13. data/lib/cancan/controller_resource_builder.rb +26 -0
  14. data/lib/cancan/controller_resource_finder.rb +42 -0
  15. data/lib/cancan/controller_resource_loader.rb +120 -0
  16. data/lib/cancan/controller_resource_name_finder.rb +23 -0
  17. data/lib/cancan/controller_resource_sanitizer.rb +32 -0
  18. data/lib/cancan/exceptions.rb +24 -4
  19. data/lib/cancan/matchers.rb +12 -1
  20. data/lib/cancan/model_adapters/abstract_adapter.rb +22 -1
  21. data/lib/cancan/model_adapters/active_record_4_adapter.rb +25 -44
  22. data/lib/cancan/model_adapters/active_record_5_adapter.rb +61 -0
  23. data/lib/cancan/model_adapters/active_record_adapter.rb +157 -83
  24. data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
  25. data/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
  26. data/lib/cancan/model_adapters/default_adapter.rb +2 -0
  27. data/lib/cancan/model_adapters/sti_normalizer.rb +47 -0
  28. data/lib/cancan/model_adapters/strategies/base.rb +40 -0
  29. data/lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb +93 -0
  30. data/lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb +31 -0
  31. data/lib/cancan/model_adapters/strategies/left_join.rb +11 -0
  32. data/lib/cancan/model_adapters/strategies/subquery.rb +18 -0
  33. data/lib/cancan/model_additions.rb +6 -2
  34. data/lib/cancan/parameter_validators.rb +9 -0
  35. data/lib/cancan/relevant.rb +29 -0
  36. data/lib/cancan/rule.rb +67 -90
  37. data/lib/cancan/rules_compressor.rb +23 -0
  38. data/lib/cancan/sti_detector.rb +12 -0
  39. data/lib/cancan/unauthorized_message_resolver.rb +24 -0
  40. data/lib/cancan/version.rb +3 -1
  41. data/lib/cancan.rb +15 -10
  42. data/lib/cancancan.rb +2 -0
  43. data/lib/generators/cancan/ability/ability_generator.rb +3 -1
  44. data/lib/generators/cancan/ability/templates/ability.rb +9 -9
  45. metadata +64 -86
  46. data/.gitignore +0 -15
  47. data/.rspec +0 -1
  48. data/.rubocop.yml +0 -39
  49. data/.rubocop_todo.yml +0 -54
  50. data/.travis.yml +0 -39
  51. data/Appraisals +0 -105
  52. data/CHANGELOG.rdoc +0 -536
  53. data/CONTRIBUTING.md +0 -23
  54. data/Gemfile +0 -3
  55. data/LICENSE +0 -22
  56. data/README.md +0 -234
  57. data/Rakefile +0 -13
  58. data/gemfiles/activerecord_3.2.gemfile +0 -18
  59. data/gemfiles/activerecord_4.0.gemfile +0 -19
  60. data/gemfiles/activerecord_4.1.gemfile +0 -19
  61. data/gemfiles/activerecord_4.2.gemfile +0 -21
  62. data/gemfiles/activerecord_5.0.gemfile +0 -20
  63. data/gemfiles/mongoid_2.x.gemfile +0 -18
  64. data/gemfiles/sequel_3.x.gemfile +0 -18
  65. data/lib/cancan/inherited_resource.rb +0 -20
  66. data/lib/cancan/model_adapters/active_record_3_adapter.rb +0 -16
  67. data/lib/cancan/model_adapters/mongoid_adapter.rb +0 -80
  68. data/lib/cancan/model_adapters/sequel_adapter.rb +0 -87
  69. data/spec/README.rdoc +0 -27
  70. data/spec/cancan/ability_spec.rb +0 -553
  71. data/spec/cancan/controller_additions_spec.rb +0 -164
  72. data/spec/cancan/controller_resource_spec.rb +0 -645
  73. data/spec/cancan/exceptions_spec.rb +0 -58
  74. data/spec/cancan/inherited_resource_spec.rb +0 -71
  75. data/spec/cancan/matchers_spec.rb +0 -29
  76. data/spec/cancan/model_adapters/active_record_4_adapter_spec.rb +0 -160
  77. data/spec/cancan/model_adapters/active_record_adapter_spec.rb +0 -415
  78. data/spec/cancan/model_adapters/default_adapter_spec.rb +0 -7
  79. data/spec/cancan/model_adapters/mongoid_adapter_spec.rb +0 -246
  80. data/spec/cancan/model_adapters/sequel_adapter_spec.rb +0 -129
  81. data/spec/cancan/rule_spec.rb +0 -52
  82. data/spec/matchers.rb +0 -13
  83. data/spec/spec.opts +0 -2
  84. data/spec/spec_helper.rb +0 -27
  85. data/spec/support/ability.rb +0 -6
@@ -0,0 +1,40 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class Base
5
+ attr_reader :adapter, :relation, :where_conditions
6
+
7
+ delegate(
8
+ :compressed_rules,
9
+ :extract_multiple_conditions,
10
+ :joins,
11
+ :model_class,
12
+ :quoted_primary_key,
13
+ :quoted_aliased_table_name,
14
+ :quoted_table_name,
15
+ to: :adapter
16
+ )
17
+ delegate :connection, :quoted_primary_key, to: :model_class
18
+ delegate :quote_table_name, to: :connection
19
+
20
+ def initialize(adapter:, relation:, where_conditions:)
21
+ @adapter = adapter
22
+ @relation = relation
23
+ @where_conditions = where_conditions
24
+ end
25
+
26
+ def aliased_table_name
27
+ @aliased_table_name ||= "#{model_class.table_name}_alias"
28
+ end
29
+
30
+ def quoted_aliased_table_name
31
+ @quoted_aliased_table_name ||= quote_table_name(aliased_table_name)
32
+ end
33
+
34
+ def quoted_table_name
35
+ @quoted_table_name ||= quote_table_name(model_class.table_name)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: false
2
+
3
+ module CanCan
4
+ module ModelAdapters
5
+ class Strategies
6
+ class JoinedAliasEachRuleAsExistsSubquery < Base
7
+ def execute!
8
+ model_class
9
+ .joins(
10
+ "JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \
11
+ "#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}"
12
+ )
13
+ .where(double_exists_sql)
14
+ end
15
+
16
+ def double_exists_sql
17
+ double_exists_sql = ''
18
+
19
+ compressed_rules.each_with_index do |rule, index|
20
+ double_exists_sql << ' OR ' if index.positive?
21
+ double_exists_sql << "EXISTS (#{sub_query_for_rule(rule).to_sql})"
22
+ end
23
+
24
+ double_exists_sql
25
+ end
26
+
27
+ def sub_query_for_rule(rule)
28
+ conditions_extractor = ConditionsExtractor.new(model_class)
29
+ rule_where_conditions = extract_multiple_conditions(conditions_extractor, [rule])
30
+ joins_hash, left_joins_hash = extract_joins_from_rule(rule)
31
+ sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash)
32
+ end
33
+
34
+ def sub_query_for_rules_and_join_hashes(rule_where_conditions, joins_hash, left_joins_hash)
35
+ model_class
36
+ .select('1')
37
+ .joins(joins_hash)
38
+ .left_joins(left_joins_hash)
39
+ .where(
40
+ "#{quoted_table_name}.#{quoted_primary_key} = " \
41
+ "#{quoted_aliased_table_name}.#{quoted_primary_key}"
42
+ )
43
+ .where(rule_where_conditions)
44
+ .limit(1)
45
+ end
46
+
47
+ def extract_joins_from_rule(rule)
48
+ joins = {}
49
+ left_joins = {}
50
+
51
+ extra_joins_recursive([], rule.conditions, joins, left_joins)
52
+ [joins, left_joins]
53
+ end
54
+
55
+ def extra_joins_recursive(current_path, conditions, joins, left_joins)
56
+ conditions.each do |key, value|
57
+ if value.is_a?(Hash)
58
+ current_path << key
59
+ extra_joins_recursive(current_path, value, joins, left_joins)
60
+ current_path.pop
61
+ else
62
+ extra_joins_recursive_merge_joins(current_path, value, joins, left_joins)
63
+ end
64
+ end
65
+ end
66
+
67
+ def extra_joins_recursive_merge_joins(current_path, value, joins, left_joins)
68
+ hash_joins = current_path_to_hash(current_path)
69
+
70
+ if value.nil?
71
+ left_joins.deep_merge!(hash_joins)
72
+ else
73
+ joins.deep_merge!(hash_joins)
74
+ end
75
+ end
76
+
77
+ # Converts an array like [:child, :grand_child] into a hash like {child: {grand_child: {}}
78
+ def current_path_to_hash(current_path)
79
+ hash_joins = {}
80
+ current_hash_joins = hash_joins
81
+
82
+ current_path.each do |path_part|
83
+ new_hash = {}
84
+ current_hash_joins[path_part] = new_hash
85
+ current_hash_joins = new_hash
86
+ end
87
+
88
+ hash_joins
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ModelAdapters
5
+ class Strategies
6
+ class JoinedAliasExistsSubquery < Base
7
+ def execute!
8
+ model_class
9
+ .joins(
10
+ "JOIN #{quoted_table_name} AS #{quoted_aliased_table_name} ON " \
11
+ "#{quoted_aliased_table_name}.#{quoted_primary_key} = #{quoted_table_name}.#{quoted_primary_key}"
12
+ )
13
+ .where("EXISTS (#{joined_alias_exists_subquery_inner_query.to_sql})")
14
+ end
15
+
16
+ def joined_alias_exists_subquery_inner_query
17
+ model_class
18
+ .unscoped
19
+ .select('1')
20
+ .left_joins(joins)
21
+ .where(*where_conditions)
22
+ .where(
23
+ "#{quoted_table_name}.#{quoted_primary_key} = " \
24
+ "#{quoted_aliased_table_name}.#{quoted_primary_key}"
25
+ )
26
+ .limit(1)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class LeftJoin < Base
5
+ def execute!
6
+ relation.left_joins(joins).distinct
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ module CanCan
2
+ module ModelAdapters
3
+ class Strategies
4
+ class Subquery < Base
5
+ def execute!
6
+ build_joins_relation_subquery(where_conditions)
7
+ end
8
+
9
+ def build_joins_relation_subquery(where_conditions)
10
+ inner = model_class.unscoped do
11
+ model_class.left_joins(joins).where(*where_conditions)
12
+ end
13
+ model_class.where(model_class.primary_key => inner)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CanCan
2
4
  # This module adds the accessible_by class method to a model. It is included in the model adapters.
3
5
  module ModelAdditions
@@ -18,8 +20,10 @@ module CanCan
18
20
  # @articles = Article.accessible_by(current_ability, :update)
19
21
  #
20
22
  # Here only the articles which the user can update are returned.
21
- def accessible_by(ability, action = :index)
22
- ability.model_adapter(self, action).database_records
23
+ def accessible_by(ability, action = :index, strategy: CanCan.accessible_by_strategy)
24
+ CanCan.with_accessible_by_strategy(strategy) do
25
+ ability.model_adapter(self, action).database_records
26
+ end
23
27
  end
24
28
  end
25
29
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module ParameterValidators
5
+ def valid_attribute_param?(attribute)
6
+ attribute.nil? || attribute.is_a?(Symbol) || (attribute.is_a?(Array) && attribute.all? { |a| a.is_a?(Symbol) })
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module Relevant
5
+ # Matches both the action, subject, and attribute, not necessarily the conditions
6
+ def relevant?(action, subject)
7
+ subject = subject.values.first if subject.class == Hash
8
+ @match_all || (matches_action?(action) && matches_subject?(subject))
9
+ end
10
+
11
+ private
12
+
13
+ def matches_action?(action)
14
+ @expanded_actions.include?(:manage) || @expanded_actions.include?(action)
15
+ end
16
+
17
+ def matches_subject?(subject)
18
+ @subjects.include?(:all) || @subjects.include?(subject) || matches_subject_class?(subject)
19
+ end
20
+
21
+ def matches_subject_class?(subject)
22
+ @subjects.any? do |sub|
23
+ sub.is_a?(Module) && (subject.is_a?(sub) ||
24
+ subject.class.to_s == sub.to_s ||
25
+ (subject.is_a?(Module) && subject.ancestors.include?(sub)))
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/cancan/rule.rb CHANGED
@@ -1,47 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conditions_matcher.rb'
4
+ require_relative 'class_matcher.rb'
5
+ require_relative 'relevant.rb'
6
+
1
7
  module CanCan
2
8
  # This class is used internally and should only be called through Ability.
3
9
  # it holds the information about a "can" call made on Ability and provides
4
10
  # helpful methods to determine permission checking and conditions hash generation.
5
11
  class Rule # :nodoc:
6
- attr_reader :base_behavior, :subjects, :actions, :conditions
7
- attr_writer :expanded_actions
12
+ include ConditionsMatcher
13
+ include Relevant
14
+ include ParameterValidators
15
+ attr_reader :base_behavior, :subjects, :actions, :conditions, :attributes, :block
16
+ attr_writer :expanded_actions, :conditions
8
17
 
9
18
  # The first argument when initializing is the base_behavior which is a true/false
10
19
  # value. True for "can" and false for "cannot". The next two arguments are the action
11
20
  # and subject respectively (such as :read, @project). The third argument is a hash
12
21
  # of conditions and the last one is the block passed to the "can" call.
13
- def initialize(base_behavior, action, subject, conditions, block)
14
- both_block_and_hash_error = 'You are not able to supply a block with a hash of conditions in '\
15
- "#{action} #{subject} ability. Use either one."
16
- raise Error, both_block_and_hash_error if conditions.is_a?(Hash) && block
22
+ def initialize(base_behavior, action, subject, *extra_args, &block)
23
+ # for backwards compatibility, attributes are an optional parameter. Check if
24
+ # attributes were passed or are actually conditions
25
+ attributes, extra_args = parse_attributes_from_extra_args(extra_args)
26
+ condition_and_block_check(extra_args, block, action, subject)
17
27
  @match_all = action.nil? && subject.nil?
28
+ raise Error, "Subject is required for #{action}" if action && subject.nil?
29
+
18
30
  @base_behavior = base_behavior
19
- @actions = [action].flatten
20
- @subjects = [subject].flatten
21
- @conditions = conditions || {}
31
+ @actions = wrap(action)
32
+ @subjects = wrap(subject)
33
+ @attributes = wrap(attributes)
34
+ @conditions = extra_args || {}
22
35
  @block = block
23
36
  end
24
37
 
25
- # Matches both the subject and action, not necessarily the conditions
26
- def relevant?(action, subject)
27
- subject = subject.values.first if subject.class == Hash
28
- @match_all || (matches_action?(action) && matches_subject?(subject))
29
- end
38
+ def inspect
39
+ repr = "#<#{self.class.name}"
40
+ repr += "#{@base_behavior ? 'can' : 'cannot'} #{@actions.inspect}, #{@subjects.inspect}, #{@attributes.inspect}"
30
41
 
31
- # Matches the block or conditions hash
32
- def matches_conditions?(action, subject, extra_args)
33
- if @match_all
34
- call_block_with_all(action, subject, extra_args)
35
- elsif @block && !subject_class?(subject)
36
- @block.call(subject, *extra_args)
37
- elsif @conditions.is_a?(Hash) && subject.class == Hash
38
- nested_subject_matches_conditions?(subject)
39
- elsif @conditions.is_a?(Hash) && !subject_class?(subject)
40
- matches_conditions_hash?(subject)
41
- else
42
- # Don't stop at "cannot" definitions when there are conditions.
43
- conditions_empty? ? true : @base_behavior
42
+ if with_scope?
43
+ repr += ", #{@conditions.where_values_hash}"
44
+ elsif [Hash, String].include?(@conditions.class)
45
+ repr += ", #{@conditions.inspect}"
44
46
  end
47
+
48
+ repr + '>'
49
+ end
50
+
51
+ def can_rule?
52
+ base_behavior
53
+ end
54
+
55
+ def cannot_catch_all?
56
+ !can_rule? && catch_all?
57
+ end
58
+
59
+ def catch_all?
60
+ (with_scope? && @conditions.where_values_hash.empty?) ||
61
+ (!with_scope? && [nil, false, [], {}, '', ' '].include?(@conditions))
45
62
  end
46
63
 
47
64
  def only_block?
@@ -52,13 +69,8 @@ module CanCan
52
69
  @block.nil? && !conditions_empty? && !@conditions.is_a?(Hash)
53
70
  end
54
71
 
55
- def conditions_empty?
56
- @conditions == {} || @conditions.nil?
57
- end
58
-
59
- def unmergeable?
60
- @conditions.respond_to?(:keys) && @conditions.present? &&
61
- (!@conditions.keys.first.is_a? Symbol)
72
+ def with_scope?
73
+ defined?(ActiveRecord) && @conditions.is_a?(ActiveRecord::Relation)
62
74
  end
63
75
 
64
76
  def associations_hash(conditions = @conditions)
@@ -81,13 +93,15 @@ module CanCan
81
93
  attributes
82
94
  end
83
95
 
84
- private
96
+ def matches_attributes?(attribute)
97
+ return true if @attributes.empty?
98
+ return @base_behavior if attribute.nil?
85
99
 
86
- def subject_class?(subject)
87
- klass = (subject.is_a?(Hash) ? subject.values.first : subject).class
88
- klass == Class || klass == Module
100
+ @attributes.include?(attribute.to_sym)
89
101
  end
90
102
 
103
+ private
104
+
91
105
  def matches_action?(action)
92
106
  @expanded_actions.include?(:manage) || @expanded_actions.include?(action)
93
107
  end
@@ -97,66 +111,29 @@ module CanCan
97
111
  end
98
112
 
99
113
  def matches_subject_class?(subject)
100
- @subjects.any? do |sub|
101
- sub.is_a?(Module) && (subject.is_a?(sub) ||
102
- subject.class.to_s == sub.to_s ||
103
- (subject.is_a?(Module) && subject.ancestors.include?(sub)))
104
- end
114
+ SubjectClassMatcher.matches_subject_class?(@subjects, subject)
105
115
  end
106
116
 
107
- # Checks if the given subject matches the given conditions hash.
108
- # This behavior can be overriden by a model adapter by defining two class methods:
109
- # override_matching_for_conditions?(subject, conditions) and
110
- # matches_conditions_hash?(subject, conditions)
111
- def matches_conditions_hash?(subject, conditions = @conditions)
112
- return true if conditions.empty?
113
- adapter = model_adapter(subject)
114
-
115
- if adapter.override_conditions_hash_matching?(subject, conditions)
116
- return adapter.matches_conditions_hash?(subject, conditions)
117
- end
118
-
119
- conditions.all? do |name, value|
120
- if adapter.override_condition_matching?(subject, name, value)
121
- adapter.matches_condition?(subject, name, value)
122
- else
123
- condition_match?(subject.send(name), value)
124
- end
125
- end
117
+ def parse_attributes_from_extra_args(args)
118
+ attributes = args.shift if valid_attribute_param?(args.first)
119
+ extra_args = args.shift
120
+ [attributes, extra_args]
126
121
  end
127
122
 
128
- def nested_subject_matches_conditions?(subject_hash)
129
- parent, _child = subject_hash.first
130
- matches_conditions_hash?(parent, @conditions[parent.class.name.downcase.to_sym] || {})
131
- end
123
+ def condition_and_block_check(conditions, block, action, subject)
124
+ return unless conditions.is_a?(Hash) && block
132
125
 
133
- def call_block_with_all(action, subject, extra_args)
134
- if subject.class == Class
135
- @block.call(action, subject, nil, *extra_args)
136
- else
137
- @block.call(action, subject.class, subject, *extra_args)
138
- end
139
- end
140
-
141
- def model_adapter(subject)
142
- CanCan::ModelAdapters::AbstractAdapter.adapter_class(subject_class?(subject) ? subject : subject.class)
143
- end
144
-
145
- def condition_match?(attribute, value)
146
- case value
147
- when Hash then hash_condition_match?(attribute, value)
148
- when String then attribute == value
149
- when Range then value.cover?(attribute)
150
- when Enumerable then value.include?(attribute)
151
- else attribute == value
152
- end
126
+ raise BlockAndConditionsError, 'A hash of conditions is mutually exclusive with a block. ' \
127
+ "Check \":#{action} #{subject}\" ability."
153
128
  end
154
129
 
155
- def hash_condition_match?(attribute, value)
156
- if attribute.is_a?(Array) || (defined?(ActiveRecord) && attribute.is_a?(ActiveRecord::Relation))
157
- attribute.any? { |element| matches_conditions_hash?(element, value) }
130
+ def wrap(object)
131
+ if object.nil?
132
+ []
133
+ elsif object.respond_to?(:to_ary)
134
+ object.to_ary || [object]
158
135
  else
159
- attribute && matches_conditions_hash?(attribute, value)
136
+ [object]
160
137
  end
161
138
  end
162
139
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'conditions_matcher.rb'
4
+ module CanCan
5
+ class RulesCompressor
6
+ attr_reader :initial_rules, :rules_collapsed
7
+
8
+ def initialize(rules)
9
+ @initial_rules = rules
10
+ @rules_collapsed = compress(@initial_rules)
11
+ end
12
+
13
+ def compress(array)
14
+ idx = array.rindex(&:catch_all?)
15
+ return array unless idx
16
+
17
+ value = array[idx]
18
+ array[idx..-1]
19
+ .drop_while { |n| n.base_behavior == value.base_behavior }
20
+ .tap { |a| a.unshift(value) unless value.cannot_catch_all? }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class StiDetector
4
+ def self.sti_class?(subject)
5
+ return false unless defined?(ActiveRecord::Base)
6
+ return false unless subject.respond_to?(:descends_from_active_record?)
7
+ return false if subject == :all || subject.descends_from_active_record?
8
+ return false unless subject < ActiveRecord::Base
9
+
10
+ true
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CanCan
4
+ module UnauthorizedMessageResolver
5
+ def unauthorized_message(action, subject)
6
+ subject = subject.values.last if subject.is_a?(Hash)
7
+ keys = unauthorized_message_keys(action, subject)
8
+ variables = {}
9
+ variables[:action] = I18n.translate("actions.#{action}", default: action.to_s)
10
+ variables[:subject] = translate_subject(subject)
11
+ message = I18n.translate(keys.shift, **variables.merge(scope: :unauthorized, default: keys + ['']))
12
+ message.blank? ? nil : message
13
+ end
14
+
15
+ def translate_subject(subject)
16
+ klass = (subject.class == Class ? subject : subject.class)
17
+ if klass.respond_to?(:model_name)
18
+ klass.model_name.human
19
+ else
20
+ klass.to_s.underscore.humanize.downcase
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module CanCan
2
- VERSION = '1.17.0'.freeze
4
+ VERSION = '3.5.0'.freeze
3
5
  end
data/lib/cancan.rb CHANGED
@@ -1,24 +1,29 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cancan/version'
4
+ require 'cancan/config'
5
+ require 'cancan/parameter_validators'
2
6
  require 'cancan/ability'
3
7
  require 'cancan/rule'
4
8
  require 'cancan/controller_resource'
5
9
  require 'cancan/controller_additions'
6
10
  require 'cancan/model_additions'
7
11
  require 'cancan/exceptions'
8
- require 'cancan/inherited_resource'
9
12
 
10
13
  require 'cancan/model_adapters/abstract_adapter'
11
14
  require 'cancan/model_adapters/default_adapter'
15
+ require 'cancan/rules_compressor'
12
16
 
13
17
  if defined? ActiveRecord
18
+ require 'cancan/model_adapters/conditions_extractor'
19
+ require 'cancan/model_adapters/conditions_normalizer'
20
+ require 'cancan/model_adapters/sti_normalizer'
14
21
  require 'cancan/model_adapters/active_record_adapter'
15
- if ActiveRecord.respond_to?(:version) &&
16
- ActiveRecord.version >= Gem::Version.new('4')
17
- require 'cancan/model_adapters/active_record_4_adapter'
18
- else
19
- require 'cancan/model_adapters/active_record_3_adapter'
20
- end
22
+ require 'cancan/model_adapters/active_record_4_adapter'
23
+ require 'cancan/model_adapters/active_record_5_adapter'
24
+ require 'cancan/model_adapters/strategies/base'
25
+ require 'cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery'
26
+ require 'cancan/model_adapters/strategies/joined_alias_exists_subquery'
27
+ require 'cancan/model_adapters/strategies/left_join'
28
+ require 'cancan/model_adapters/strategies/subquery'
21
29
  end
22
-
23
- require 'cancan/model_adapters/mongoid_adapter' if defined?(Mongoid) && defined?(Mongoid::Document)
24
- require 'cancan/model_adapters/sequel_adapter' if defined? Sequel
data/lib/cancancan.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cancan'
2
4
 
3
5
  module CanCanCan
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cancan
2
4
  module Generators
3
5
  class AbilityGenerator < Rails::Generators::Base
4
- source_root File.expand_path('../templates', __FILE__)
6
+ source_root File.expand_path('templates', __dir__)
5
7
 
6
8
  def generate_ability
7
9
  copy_file 'ability.rb', 'app/models/ability.rb'
@@ -1,15 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Ability
2
4
  include CanCan::Ability
3
5
 
4
6
  def initialize(user)
5
- # Define abilities for the passed in user here. For example:
7
+ # Define abilities for the user here. For example:
6
8
  #
7
- # user ||= User.new # guest user (not logged in)
8
- # if user.admin?
9
- # can :manage, :all
10
- # else
11
- # can :read, :all
12
- # end
9
+ # return unless user.present?
10
+ # can :read, :all
11
+ # return unless user.admin?
12
+ # can :manage, :all
13
13
  #
14
14
  # The first argument to `can` is the action you are giving the user
15
15
  # permission to do.
@@ -24,9 +24,9 @@ class Ability
24
24
  # objects.
25
25
  # For example, here the user can only update published articles.
26
26
  #
27
- # can :update, Article, :published => true
27
+ # can :update, Article, published: true
28
28
  #
29
29
  # See the wiki for details:
30
- # https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities
30
+ # https://github.com/CanCanCommunity/cancancan/blob/develop/docs/define_check_abilities.md
31
31
  end
32
32
  end