cancancan 2.2.0 → 2.3.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/controller_additions.rb +4 -2
- data/lib/cancan/exceptions.rb +3 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +10 -36
- data/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb +1 -1
- data/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
- data/lib/cancan/rule.rb +12 -0
- data/lib/cancan/rules_compressor.rb +20 -0
- data/lib/cancan/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f63f1d6ab266068caab6e7baf2b628ae72a7e03a134116eda91473861b9df359
|
4
|
+
data.tar.gz: fbf6337f058ece9a2f01c990a55398489e0ea56888f0a5ce3b9f8d5e203c31ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24fcc98ce0592b263add65cf0bc75a7020d75777fea7c6902216f97bbc9c13a36b4d379194a142cd8a5e5a24dc1ae82c82a61d6d99143c30a9afe11133048528
|
7
|
+
data.tar.gz: 8873b440698a941314f67a748db86c2abe33e89417ae54d9f35769c29f904edc9eff5736d0bf55d53badc494b9bca9e0ac1761c1920067238628d23dc8e0c643
|
data/lib/cancan.rb
CHANGED
@@ -8,8 +8,10 @@ require 'cancan/exceptions'
|
|
8
8
|
|
9
9
|
require 'cancan/model_adapters/abstract_adapter'
|
10
10
|
require 'cancan/model_adapters/default_adapter'
|
11
|
+
require 'cancan/rules_compressor'
|
11
12
|
|
12
13
|
if defined? ActiveRecord
|
14
|
+
require 'cancan/model_adapters/conditions_extractor'
|
13
15
|
require 'cancan/model_adapters/active_record_adapter'
|
14
16
|
require 'cancan/model_adapters/active_record_4_adapter'
|
15
17
|
require 'cancan/model_adapters/active_record_5_adapter'
|
@@ -384,6 +384,8 @@ module CanCan
|
|
384
384
|
end
|
385
385
|
end
|
386
386
|
|
387
|
-
ActiveSupport
|
388
|
-
|
387
|
+
if defined? ActiveSupport
|
388
|
+
ActiveSupport.on_load(:action_controller) do
|
389
|
+
include CanCan::ControllerAdditions
|
390
|
+
end
|
389
391
|
end
|
data/lib/cancan/exceptions.rb
CHANGED
@@ -11,6 +11,9 @@ module CanCan
|
|
11
11
|
# Raised when using check_authorization without calling authorized!
|
12
12
|
class AuthorizationNotPerformed < Error; end
|
13
13
|
|
14
|
+
# Raised when using a wrong association name
|
15
|
+
class WrongAssociationName < Error; end
|
16
|
+
|
14
17
|
# This error is raised when a user isn't allowed to access a given controller action.
|
15
18
|
# This usually happens within a call to ControllerAdditions#authorize! but can be
|
16
19
|
# raised manually.
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require_relative 'can_can/model_adapters/active_record_adapter/joins.rb'
|
2
|
+
require_relative 'conditions_extractor.rb'
|
3
|
+
require 'cancan/rules_compressor'
|
2
4
|
module CanCan
|
3
5
|
module ModelAdapters
|
4
6
|
module ActiveRecordAdapter
|
@@ -20,47 +22,19 @@ module CanCan
|
|
20
22
|
# query(:manage, User).conditions # => "not (self_managed = 't') AND ((manager_id = 1) OR (id = 1))"
|
21
23
|
#
|
22
24
|
def conditions
|
23
|
-
|
25
|
+
compressed_rules = RulesCompressor.new(@rules.reverse).rules_collapsed.reverse
|
26
|
+
conditions_extractor = ConditionsExtractor.new(@model_class)
|
27
|
+
if compressed_rules.size == 1 && compressed_rules.first.base_behavior
|
24
28
|
# Return the conditions directly if there's just one definition
|
25
|
-
|
29
|
+
conditions_extractor.tableize_conditions(compressed_rules.first.conditions).dup
|
26
30
|
else
|
27
|
-
extract_multiple_conditions
|
31
|
+
extract_multiple_conditions(conditions_extractor, compressed_rules)
|
28
32
|
end
|
29
33
|
end
|
30
34
|
|
31
|
-
def extract_multiple_conditions
|
32
|
-
|
33
|
-
merge_conditions(sql,
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def tableized_conditions(conditions, model_class = @model_class)
|
38
|
-
return conditions unless conditions.is_a? Hash
|
39
|
-
conditions.each_with_object({}) do |(name, value), result_hash|
|
40
|
-
calculate_result_hash(model_class, name, result_hash, value)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def calculate_result_hash(model_class, name, result_hash, value)
|
45
|
-
if value.is_a? Hash
|
46
|
-
association_class = model_class.reflect_on_association(name).klass.name.constantize
|
47
|
-
nested_resulted = calculate_nested(model_class, name, result_hash, value.dup)
|
48
|
-
result_hash.merge!(tableized_conditions(nested_resulted, association_class))
|
49
|
-
else
|
50
|
-
result_hash[name] = value
|
51
|
-
end
|
52
|
-
result_hash
|
53
|
-
end
|
54
|
-
|
55
|
-
def calculate_nested(model_class, name, result_hash, value)
|
56
|
-
value.each_with_object({}) do |(k, v), nested|
|
57
|
-
if v.is_a? Hash
|
58
|
-
value.delete(k)
|
59
|
-
nested[k] = v
|
60
|
-
else
|
61
|
-
result_hash[model_class.reflect_on_association(name).table_name.to_sym] = value
|
62
|
-
end
|
63
|
-
nested
|
35
|
+
def extract_multiple_conditions(conditions_extractor, rules)
|
36
|
+
rules.reverse.inject(false_sql) do |sql, rule|
|
37
|
+
merge_conditions(sql, conditions_extractor.tableize_conditions(rule.conditions).dup, rule.base_behavior)
|
64
38
|
end
|
65
39
|
end
|
66
40
|
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# this class is responsible of converting the hash of conditions
|
2
|
+
# in "where conditions" to generate the sql query
|
3
|
+
# it consists of a names_cache that helps calculating the next name given to the association
|
4
|
+
# it tries to reflect the bahavior of ActiveRecord when generating aliases for tables.
|
5
|
+
module CanCan
|
6
|
+
module ModelAdapters
|
7
|
+
class ConditionsExtractor
|
8
|
+
def initialize(model_class)
|
9
|
+
@names_cache = { model_class.table_name => [] }.with_indifferent_access
|
10
|
+
@root_model_class = model_class
|
11
|
+
end
|
12
|
+
|
13
|
+
def tableize_conditions(conditions, model_class = @root_model_class, path_to_key = 0)
|
14
|
+
return conditions unless conditions.is_a? Hash
|
15
|
+
conditions.each_with_object({}) do |(key, value), result_hash|
|
16
|
+
if value.is_a? Hash
|
17
|
+
result_hash.merge!(calculate_result_hash(key, model_class, path_to_key, result_hash, value))
|
18
|
+
else
|
19
|
+
result_hash[key] = value
|
20
|
+
end
|
21
|
+
result_hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def calculate_result_hash(key, model_class, path_to_key, result_hash, value)
|
28
|
+
reflection = model_class.reflect_on_association(key)
|
29
|
+
unless reflection
|
30
|
+
raise WrongAssociationName, "association #{key} not defined in model #{model_class.name}"
|
31
|
+
end
|
32
|
+
nested_resulted = calculate_nested(model_class, result_hash, key, value.dup, path_to_key)
|
33
|
+
association_class = reflection.klass.name.constantize
|
34
|
+
tableize_conditions(nested_resulted, association_class, "#{path_to_key}_#{key}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def calculate_nested(model_class, result_hash, relation_name, value, path_to_key)
|
38
|
+
value.each_with_object({}) do |(k, v), nested|
|
39
|
+
if v.is_a? Hash
|
40
|
+
value.delete(k)
|
41
|
+
nested[k] = v
|
42
|
+
else
|
43
|
+
table_alias = generate_table_alias(model_class, relation_name, path_to_key)
|
44
|
+
result_hash[table_alias] = value
|
45
|
+
end
|
46
|
+
nested
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_table_alias(model_class, relation_name, path_to_key)
|
51
|
+
table_alias = model_class.reflect_on_association(relation_name).table_name.to_sym
|
52
|
+
|
53
|
+
if alredy_used?(table_alias, relation_name, path_to_key)
|
54
|
+
table_alias = "#{relation_name.to_s.pluralize}_#{model_class.table_name}".to_sym
|
55
|
+
|
56
|
+
index = 1
|
57
|
+
while alredy_used?(table_alias, relation_name, path_to_key)
|
58
|
+
table_alias = "#{table_alias}_#{index += 1}".to_sym
|
59
|
+
end
|
60
|
+
end
|
61
|
+
add_to_cache(table_alias, relation_name, path_to_key)
|
62
|
+
end
|
63
|
+
|
64
|
+
def alredy_used?(table_alias, relation_name, path_to_key)
|
65
|
+
@names_cache[table_alias].try(:exclude?, "#{path_to_key}_#{relation_name}")
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_to_cache(table_alias, relation_name, path_to_key)
|
69
|
+
@names_cache[table_alias] ||= []
|
70
|
+
@names_cache[table_alias] << "#{path_to_key}_#{relation_name}"
|
71
|
+
table_alias
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/cancan/rule.rb
CHANGED
@@ -24,6 +24,18 @@ module CanCan
|
|
24
24
|
@block = block
|
25
25
|
end
|
26
26
|
|
27
|
+
def can_rule?
|
28
|
+
base_behavior
|
29
|
+
end
|
30
|
+
|
31
|
+
def cannot_catch_all?
|
32
|
+
!can_rule? && catch_all?
|
33
|
+
end
|
34
|
+
|
35
|
+
def catch_all?
|
36
|
+
[nil, false, [], {}, '', ' '].include? @conditions
|
37
|
+
end
|
38
|
+
|
27
39
|
# Matches both the subject and action, not necessarily the conditions
|
28
40
|
def relevant?(action, subject)
|
29
41
|
subject = subject.values.first if subject.class == Hash
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative 'conditions_matcher.rb'
|
2
|
+
module CanCan
|
3
|
+
class RulesCompressor
|
4
|
+
attr_reader :initial_rules, :rules_collapsed
|
5
|
+
|
6
|
+
def initialize(rules)
|
7
|
+
@initial_rules = rules
|
8
|
+
@rules_collapsed = compress(@initial_rules)
|
9
|
+
end
|
10
|
+
|
11
|
+
def compress(array)
|
12
|
+
idx = array.rindex(&:catch_all?)
|
13
|
+
return array unless idx
|
14
|
+
value = array[idx]
|
15
|
+
array[idx..-1]
|
16
|
+
.drop_while { |n| n.base_behavior == value.base_behavior }
|
17
|
+
.tap { |a| a.unshift(value) unless value.cannot_catch_all? }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/cancan/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cancancan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alessandro Rodi (Renuo AG)
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2018-
|
14
|
+
date: 2018-09-16 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -129,9 +129,11 @@ files:
|
|
129
129
|
- lib/cancan/model_adapters/active_record_5_adapter.rb
|
130
130
|
- lib/cancan/model_adapters/active_record_adapter.rb
|
131
131
|
- lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb
|
132
|
+
- lib/cancan/model_adapters/conditions_extractor.rb
|
132
133
|
- lib/cancan/model_adapters/default_adapter.rb
|
133
134
|
- lib/cancan/model_additions.rb
|
134
135
|
- lib/cancan/rule.rb
|
136
|
+
- lib/cancan/rules_compressor.rb
|
135
137
|
- lib/cancan/version.rb
|
136
138
|
- lib/cancancan.rb
|
137
139
|
- lib/generators/cancan/ability/USAGE
|