cancancan 3.1.0 → 3.2.0
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 +4 -4
- data/lib/cancan.rb +2 -0
- data/lib/cancan/ability.rb +5 -1
- data/lib/cancan/class_matcher.rb +26 -0
- data/lib/cancan/conditions_matcher.rb +1 -1
- data/lib/cancan/config.rb +49 -0
- data/lib/cancan/exceptions.rb +8 -0
- data/lib/cancan/model_adapters/abstract_adapter.rb +1 -1
- data/lib/cancan/model_adapters/active_record_4_adapter.rb +2 -4
- data/lib/cancan/model_adapters/active_record_5_adapter.rb +7 -10
- data/lib/cancan/model_adapters/active_record_adapter.rb +9 -2
- data/lib/cancan/model_adapters/sti_normalizer.rb +31 -0
- data/lib/cancan/rule.rb +14 -1
- data/lib/cancan/version.rb +1 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f912e7e8ba7143a52b467949b374103edb0aae0b8b3dcbea4157400b6a70e7d6
|
4
|
+
data.tar.gz: 604a95ca8d9a794386810ad68a98eec86d1b6a8b73c07d64c0b8706ec5c52fa6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5988dd5e020a13b020769f632a4c780f2b3a401c6064f9d4b1da7f4d3570cf18d52be603ed2fd55059b88f5b06e4dd6fb6708e893ff899e2ffbd793eddfec07a
|
7
|
+
data.tar.gz: 5c2338a84835d7d828739984925bd2906f044d8b0c0355a0a62a8fe763e2f203e579e4909ba37fdbf8f30bce26e36790a47793ab056fb39e7a6c1e683ce9e486
|
data/lib/cancan.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'cancan/version'
|
4
|
+
require 'cancan/config'
|
4
5
|
require 'cancan/parameter_validators'
|
5
6
|
require 'cancan/ability'
|
6
7
|
require 'cancan/rule'
|
@@ -16,6 +17,7 @@ require 'cancan/rules_compressor'
|
|
16
17
|
if defined? ActiveRecord
|
17
18
|
require 'cancan/model_adapters/conditions_extractor'
|
18
19
|
require 'cancan/model_adapters/conditions_normalizer'
|
20
|
+
require 'cancan/model_adapters/sti_normalizer'
|
19
21
|
require 'cancan/model_adapters/active_record_adapter'
|
20
22
|
require 'cancan/model_adapters/active_record_4_adapter'
|
21
23
|
require 'cancan/model_adapters/active_record_5_adapter'
|
data/lib/cancan/ability.rb
CHANGED
@@ -302,7 +302,11 @@ module CanCan
|
|
302
302
|
|
303
303
|
def alternative_subjects(subject)
|
304
304
|
subject = subject.class unless subject.is_a?(Module)
|
305
|
-
|
305
|
+
if subject.respond_to?(:subclasses) && 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
|
306
310
|
end
|
307
311
|
end
|
308
312
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# This class is responsible for matching classes and their subclasses as well as
|
2
|
+
# upmatching classes to their ancestors.
|
3
|
+
# This is used to generate sti connections
|
4
|
+
class SubjectClassMatcher
|
5
|
+
def self.matches_subject_class?(subjects, subject)
|
6
|
+
subjects.any? do |sub|
|
7
|
+
has_subclasses = subject.respond_to?(:subclasses)
|
8
|
+
matching_class_check(subject, sub, has_subclasses)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.matching_class_check(subject, sub, has_subclasses)
|
13
|
+
matches = matches_class_or_is_related(subject, sub)
|
14
|
+
if has_subclasses
|
15
|
+
matches || subject.subclasses.include?(sub)
|
16
|
+
else
|
17
|
+
matches
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.matches_class_or_is_related(subject, sub)
|
22
|
+
sub.is_a?(Module) && (subject.is_a?(sub) ||
|
23
|
+
subject.class.to_s == sub.to_s ||
|
24
|
+
(subject.is_a?(Module) && subject.ancestors.include?(sub)))
|
25
|
+
end
|
26
|
+
end
|
@@ -78,7 +78,7 @@ module CanCan
|
|
78
78
|
|
79
79
|
def hash_condition_match?(attribute, value)
|
80
80
|
if attribute.is_a?(Array) || (defined?(ActiveRecord) && attribute.is_a?(ActiveRecord::Relation))
|
81
|
-
attribute.any? { |element| matches_conditions_hash?(element, value) }
|
81
|
+
attribute.to_a.any? { |element| matches_conditions_hash?(element, value) }
|
82
82
|
else
|
83
83
|
attribute && matches_conditions_hash?(attribute, value)
|
84
84
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CanCan
|
4
|
+
def self.valid_accessible_by_strategies
|
5
|
+
strategies = [:left_join]
|
6
|
+
strategies << :subquery unless CanCan::ModelAdapters::ActiveRecordAdapter.version_lower?('5.0.0')
|
7
|
+
strategies
|
8
|
+
end
|
9
|
+
|
10
|
+
# Determines how CanCan should build queries when calling accessible_by,
|
11
|
+
# if the query will contain a join. The default strategy is `:subquery`.
|
12
|
+
#
|
13
|
+
# # config/initializers/cancan.rb
|
14
|
+
# CanCan.accessible_by_strategy = :subquery
|
15
|
+
#
|
16
|
+
# Valid strategies are:
|
17
|
+
# - :subquery - Creates a nested query with all joins, wrapped by a
|
18
|
+
# WHERE IN query.
|
19
|
+
# - :left_join - Calls the joins directly using `left_joins`, and
|
20
|
+
# ensures records are unique using `distinct`. Note that
|
21
|
+
# `distinct` is not reliable in some cases. See
|
22
|
+
# https://github.com/CanCanCommunity/cancancan/pull/605
|
23
|
+
def self.accessible_by_strategy
|
24
|
+
@accessible_by_strategy || default_accessible_by_strategy
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.default_accessible_by_strategy
|
28
|
+
if CanCan::ModelAdapters::ActiveRecordAdapter.version_lower?('5.0.0')
|
29
|
+
# see https://github.com/CanCanCommunity/cancancan/pull/655 for where this was added
|
30
|
+
# the `subquery` strategy (from https://github.com/CanCanCommunity/cancancan/pull/619
|
31
|
+
# only works in Rails 5 and higher
|
32
|
+
:left_join
|
33
|
+
else
|
34
|
+
:subquery
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.accessible_by_strategy=(value)
|
39
|
+
unless valid_accessible_by_strategies.include?(value)
|
40
|
+
raise ArgumentError, "accessible_by_strategy must be one of #{valid_accessible_by_strategies.join(', ')}"
|
41
|
+
end
|
42
|
+
|
43
|
+
if value == :subquery && CanCan::ModelAdapters::ActiveRecordAdapter.version_lower?('5.0.0')
|
44
|
+
raise ArgumentError, 'accessible_by_strategy = :subquery requires ActiveRecord 5 or newer'
|
45
|
+
end
|
46
|
+
|
47
|
+
@accessible_by_strategy = value
|
48
|
+
end
|
49
|
+
end
|
data/lib/cancan/exceptions.rb
CHANGED
@@ -58,5 +58,13 @@ module CanCan
|
|
58
58
|
def to_s
|
59
59
|
@message || @default_message
|
60
60
|
end
|
61
|
+
|
62
|
+
def inspect
|
63
|
+
details = %i[action subject conditions message].map do |attribute|
|
64
|
+
value = instance_variable_get "@#{attribute}"
|
65
|
+
"#{attribute}: #{value.inspect}" if value.present?
|
66
|
+
end.compact.join(', ')
|
67
|
+
"#<#{self.class.name} #{details}>"
|
68
|
+
end
|
61
69
|
end
|
62
70
|
end
|
@@ -34,10 +34,8 @@ module CanCan
|
|
34
34
|
# look inside the where clause to decide to outer join tables
|
35
35
|
# you're using in the where. Instead, `references()` is required
|
36
36
|
# in addition to `includes()` to force the outer join.
|
37
|
-
def
|
38
|
-
relation
|
39
|
-
relation = relation.includes(joins).references(joins) if joins.present?
|
40
|
-
relation
|
37
|
+
def build_joins_relation(relation, *_where_conditions)
|
38
|
+
relation.includes(joins).references(joins)
|
41
39
|
end
|
42
40
|
|
43
41
|
# Rails 4.2 deprecates `sanitize_sql_hash_for_conditions`
|
@@ -21,18 +21,19 @@ module CanCan
|
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
-
def
|
25
|
-
|
24
|
+
def build_joins_relation(relation, *where_conditions)
|
25
|
+
case CanCan.accessible_by_strategy
|
26
|
+
when :subquery
|
26
27
|
inner = @model_class.unscoped do
|
27
28
|
@model_class.left_joins(joins).where(*where_conditions)
|
28
29
|
end
|
29
30
|
@model_class.where(@model_class.primary_key => inner)
|
30
|
-
|
31
|
-
|
31
|
+
|
32
|
+
when :left_join
|
33
|
+
relation.left_joins(joins).distinct
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
35
|
-
# Rails 4.2 deprecates `sanitize_sql_hash_for_conditions`
|
36
37
|
def sanitize_sql(conditions)
|
37
38
|
if conditions.is_a?(Hash)
|
38
39
|
sanitize_sql_activerecord5(conditions)
|
@@ -46,11 +47,7 @@ module CanCan
|
|
46
47
|
table_metadata = ActiveRecord::TableMetadata.new(@model_class, table)
|
47
48
|
predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata)
|
48
49
|
|
49
|
-
conditions
|
50
|
-
|
51
|
-
conditions.stringify_keys!
|
52
|
-
|
53
|
-
predicate_builder.build_from_hash(conditions).map { |b| visit_nodes(b) }.join(' AND ')
|
50
|
+
predicate_builder.build_from_hash(conditions.stringify_keys).map { |b| visit_nodes(b) }.join(' AND ')
|
54
51
|
end
|
55
52
|
|
56
53
|
def visit_nodes(node)
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'conditions_extractor.rb'
|
4
|
-
require 'cancan/rules_compressor'
|
5
3
|
module CanCan
|
6
4
|
module ModelAdapters
|
7
5
|
class ActiveRecordAdapter < AbstractAdapter
|
@@ -16,6 +14,7 @@ module CanCan
|
|
16
14
|
def initialize(model_class, rules)
|
17
15
|
super
|
18
16
|
@compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
|
17
|
+
StiNormalizer.normalize(@compressed_rules)
|
19
18
|
ConditionsNormalizer.normalize(model_class, @compressed_rules)
|
20
19
|
end
|
21
20
|
|
@@ -60,6 +59,14 @@ module CanCan
|
|
60
59
|
end
|
61
60
|
end
|
62
61
|
|
62
|
+
def build_relation(*where_conditions)
|
63
|
+
relation = @model_class.where(*where_conditions)
|
64
|
+
return relation unless joins.present?
|
65
|
+
|
66
|
+
# subclasses must implement `build_joins_relation`
|
67
|
+
build_joins_relation(relation, *where_conditions)
|
68
|
+
end
|
69
|
+
|
63
70
|
# Returns the associations used in conditions for the :joins option of a search.
|
64
71
|
# See ModelAdditions#accessible_by
|
65
72
|
def joins
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# this class is responsible for detecting sti classes and creating new rules for the
|
2
|
+
# relevant subclasses, using the inheritance_column as a merger
|
3
|
+
module CanCan
|
4
|
+
module ModelAdapters
|
5
|
+
class StiNormalizer
|
6
|
+
class << self
|
7
|
+
def normalize(rules)
|
8
|
+
rules_cache = []
|
9
|
+
rules.delete_if.with_index do |rule, _index|
|
10
|
+
subjects = rule.subjects.select do |subject|
|
11
|
+
next if subject == :all || subject.descends_from_active_record?
|
12
|
+
|
13
|
+
rules_cache.push(build_rule_for_subclass(rule, subject))
|
14
|
+
true
|
15
|
+
end
|
16
|
+
subjects.length == rule.subjects.length
|
17
|
+
end
|
18
|
+
rules_cache.each { |rule| rules.push(rule) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# create a new rule for the subclasses that links on the inheritance_column
|
24
|
+
def build_rule_for_subclass(rule, subject)
|
25
|
+
CanCan::Rule.new(rule.base_behavior, rule.actions, subject.superclass,
|
26
|
+
rule.conditions.merge(subject.inheritance_column => subject.name), rule.block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/cancan/rule.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'conditions_matcher.rb'
|
4
|
+
require_relative 'class_matcher.rb'
|
4
5
|
require_relative 'relevant.rb'
|
5
6
|
|
6
7
|
module CanCan
|
@@ -11,7 +12,7 @@ module CanCan
|
|
11
12
|
include ConditionsMatcher
|
12
13
|
include Relevant
|
13
14
|
include ParameterValidators
|
14
|
-
attr_reader :base_behavior, :subjects, :actions, :conditions, :attributes
|
15
|
+
attr_reader :base_behavior, :subjects, :actions, :conditions, :attributes, :block
|
15
16
|
attr_writer :expanded_actions, :conditions
|
16
17
|
|
17
18
|
# The first argument when initializing is the base_behavior which is a true/false
|
@@ -101,6 +102,18 @@ module CanCan
|
|
101
102
|
|
102
103
|
private
|
103
104
|
|
105
|
+
def matches_action?(action)
|
106
|
+
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
107
|
+
end
|
108
|
+
|
109
|
+
def matches_subject?(subject)
|
110
|
+
@subjects.include?(:all) || @subjects.include?(subject) || matches_subject_class?(subject)
|
111
|
+
end
|
112
|
+
|
113
|
+
def matches_subject_class?(subject)
|
114
|
+
SubjectClassMatcher.matches_subject_class?(@subjects, subject)
|
115
|
+
end
|
116
|
+
|
104
117
|
def parse_attributes_from_extra_args(args)
|
105
118
|
attributes = args.shift if valid_attribute_param?(args.first)
|
106
119
|
extra_args = args.shift
|
data/lib/cancan/version.rb
CHANGED
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cancancan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Rodi (Renuo AG)
|
8
8
|
- Bryan Rite
|
9
9
|
- Ryan Bates
|
10
10
|
- Richard Wilson
|
11
|
-
autorequire:
|
11
|
+
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-12-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: appraisal
|
@@ -115,7 +115,9 @@ files:
|
|
115
115
|
- lib/cancan/ability/actions.rb
|
116
116
|
- lib/cancan/ability/rules.rb
|
117
117
|
- lib/cancan/ability/strong_parameter_support.rb
|
118
|
+
- lib/cancan/class_matcher.rb
|
118
119
|
- lib/cancan/conditions_matcher.rb
|
120
|
+
- lib/cancan/config.rb
|
119
121
|
- lib/cancan/controller_additions.rb
|
120
122
|
- lib/cancan/controller_resource.rb
|
121
123
|
- lib/cancan/controller_resource_builder.rb
|
@@ -132,6 +134,7 @@ files:
|
|
132
134
|
- lib/cancan/model_adapters/conditions_extractor.rb
|
133
135
|
- lib/cancan/model_adapters/conditions_normalizer.rb
|
134
136
|
- lib/cancan/model_adapters/default_adapter.rb
|
137
|
+
- lib/cancan/model_adapters/sti_normalizer.rb
|
135
138
|
- lib/cancan/model_additions.rb
|
136
139
|
- lib/cancan/parameter_validators.rb
|
137
140
|
- lib/cancan/relevant.rb
|
@@ -147,7 +150,7 @@ homepage: https://github.com/CanCanCommunity/cancancan
|
|
147
150
|
licenses:
|
148
151
|
- MIT
|
149
152
|
metadata: {}
|
150
|
-
post_install_message:
|
153
|
+
post_install_message:
|
151
154
|
rdoc_options: []
|
152
155
|
require_paths:
|
153
156
|
- lib
|
@@ -163,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
166
|
version: '0'
|
164
167
|
requirements: []
|
165
168
|
rubygems_version: 3.0.6
|
166
|
-
signing_key:
|
169
|
+
signing_key:
|
167
170
|
specification_version: 4
|
168
171
|
summary: Simple authorization solution for Rails.
|
169
172
|
test_files: []
|