cancancan 2.1.0 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cancan.rb +1 -1
- data/lib/cancan/ability.rb +13 -175
- data/lib/cancan/ability/actions.rb +91 -0
- data/lib/cancan/ability/rules.rb +85 -0
- data/lib/cancan/conditions_matcher.rb +93 -0
- data/lib/cancan/controller_resource.rb +22 -207
- data/lib/cancan/controller_resource_builder.rb +24 -0
- data/lib/cancan/controller_resource_finder.rb +35 -0
- data/lib/cancan/controller_resource_loader.rb +116 -0
- data/lib/cancan/controller_resource_name_finder.rb +21 -0
- data/lib/cancan/controller_resource_sanitizer.rb +30 -0
- data/lib/cancan/model_adapters/active_record_adapter.rb +55 -70
- data/lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb +39 -0
- data/lib/cancan/rule.rb +4 -83
- data/lib/cancan/version.rb +1 -1
- metadata +11 -2
@@ -0,0 +1,21 @@
|
|
1
|
+
module CanCan
|
2
|
+
module ControllerResourceNameFinder
|
3
|
+
protected
|
4
|
+
|
5
|
+
def name_from_controller
|
6
|
+
@params[:controller].split('/').last.singularize
|
7
|
+
end
|
8
|
+
|
9
|
+
def namespaced_name
|
10
|
+
[namespace, name].join('/').singularize.camelize.safe_constantize || name
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
@name || name_from_controller
|
15
|
+
end
|
16
|
+
|
17
|
+
def namespace
|
18
|
+
@params[:controller].split('/')[0..-2]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module CanCan
|
2
|
+
module ControllerResourceSanitizer
|
3
|
+
protected
|
4
|
+
|
5
|
+
def sanitize_parameters
|
6
|
+
case params_method
|
7
|
+
when Symbol
|
8
|
+
@controller.send(params_method)
|
9
|
+
when String
|
10
|
+
@controller.instance_eval(params_method)
|
11
|
+
when Proc
|
12
|
+
params_method.call(@controller)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def params_methods
|
17
|
+
methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
|
18
|
+
methods.unshift(@options[:param_method]) if @options[:param_method].present?
|
19
|
+
methods
|
20
|
+
end
|
21
|
+
|
22
|
+
def params_method
|
23
|
+
params_methods.each do |method|
|
24
|
+
return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) ||
|
25
|
+
method.is_a?(String) || method.is_a?(Proc)
|
26
|
+
end
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
|
+
require_relative 'can_can/model_adapters/active_record_adapter/joins.rb'
|
1
2
|
module CanCan
|
2
3
|
module ModelAdapters
|
3
4
|
module ActiveRecordAdapter
|
5
|
+
include CanCan::ModelAdapters::ActiveRecordAdapter::Joins
|
6
|
+
|
4
7
|
# Returns conditions intended to be used inside a database query. Normally you will not call this
|
5
8
|
# method directly, but instead go through ModelAdditions#accessible_by.
|
6
9
|
#
|
@@ -21,54 +24,51 @@ module CanCan
|
|
21
24
|
# Return the conditions directly if there's just one definition
|
22
25
|
tableized_conditions(@rules.first.conditions).dup
|
23
26
|
else
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
+
extract_multiple_conditions
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def extract_multiple_conditions
|
32
|
+
@rules.reverse.inject(false_sql) do |sql, rule|
|
33
|
+
merge_conditions(sql, tableized_conditions(rule.conditions).dup, rule.base_behavior)
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
30
37
|
def tableized_conditions(conditions, model_class = @model_class)
|
31
38
|
return conditions unless conditions.is_a? Hash
|
32
39
|
conditions.each_with_object({}) do |(name, value), result_hash|
|
33
|
-
|
34
|
-
value = value.dup
|
35
|
-
association_class = model_class.reflect_on_association(name).klass.name.constantize
|
36
|
-
nested_resulted = value.each_with_object({}) do |(k, v), nested|
|
37
|
-
if v.is_a? Hash
|
38
|
-
value.delete(k)
|
39
|
-
nested[k] = v
|
40
|
-
else
|
41
|
-
result_hash[model_class.reflect_on_association(name).table_name.to_sym] = value
|
42
|
-
end
|
43
|
-
nested
|
44
|
-
end
|
45
|
-
result_hash.merge!(tableized_conditions(nested_resulted, association_class))
|
46
|
-
else
|
47
|
-
result_hash[name] = value
|
48
|
-
end
|
49
|
-
result_hash
|
40
|
+
calculate_result_hash(model_class, name, result_hash, value)
|
50
41
|
end
|
51
42
|
end
|
52
43
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
59
64
|
end
|
60
|
-
clean_joins(joins_hash) unless joins_hash.empty?
|
61
65
|
end
|
62
66
|
|
63
67
|
def database_records
|
64
68
|
if override_scope
|
65
69
|
@model_class.where(nil).merge(override_scope)
|
66
70
|
elsif @model_class.respond_to?(:where) && @model_class.respond_to?(:joins)
|
67
|
-
|
68
|
-
build_relation(conditions)
|
69
|
-
else
|
70
|
-
build_relation(*@rules.map(&:conditions))
|
71
|
-
end
|
71
|
+
mergeable_conditions? ? build_relation(conditions) : build_relation(*@rules.map(&:conditions))
|
72
72
|
else
|
73
73
|
@model_class.all(conditions: conditions, joins: joins)
|
74
74
|
end
|
@@ -82,30 +82,35 @@ module CanCan
|
|
82
82
|
|
83
83
|
def override_scope
|
84
84
|
conditions = @rules.map(&:conditions).compact
|
85
|
-
return unless
|
86
|
-
if conditions.size == 1
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
85
|
+
return unless conditions.any? { |c| c.is_a?(ActiveRecord::Relation) }
|
86
|
+
return conditions.first if conditions.size == 1
|
87
|
+
raise_override_scope_error
|
88
|
+
end
|
89
|
+
|
90
|
+
def raise_override_scope_error
|
91
|
+
rule_found = @rules.detect { |rule| rule.conditions.is_a?(ActiveRecord::Relation) }
|
92
|
+
raise Error,
|
93
|
+
'Unable to merge an Active Record scope with other conditions. '\
|
94
|
+
"Instead use a hash or SQL for #{rule_found.actions.first} #{rule_found.subjects.first} ability."
|
94
95
|
end
|
95
96
|
|
96
97
|
def merge_conditions(sql, conditions_hash, behavior)
|
97
98
|
if conditions_hash.blank?
|
98
99
|
behavior ? true_sql : false_sql
|
99
100
|
else
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
101
|
+
merge_non_empty_conditions(behavior, conditions_hash, sql)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def merge_non_empty_conditions(behavior, conditions_hash, sql)
|
106
|
+
conditions = sanitize_sql(conditions_hash)
|
107
|
+
case sql
|
108
|
+
when true_sql
|
109
|
+
behavior ? true_sql : "not (#{conditions})"
|
110
|
+
when false_sql
|
111
|
+
behavior ? conditions : false_sql
|
112
|
+
else
|
113
|
+
behavior ? "(#{conditions}) OR (#{sql})" : "not (#{conditions}) AND (#{sql})"
|
109
114
|
end
|
110
115
|
end
|
111
116
|
|
@@ -120,26 +125,6 @@ module CanCan
|
|
120
125
|
def sanitize_sql(conditions)
|
121
126
|
@model_class.send(:sanitize_sql, conditions)
|
122
127
|
end
|
123
|
-
|
124
|
-
# Takes two hashes and does a deep merge.
|
125
|
-
def merge_joins(base, add)
|
126
|
-
add.each do |name, nested|
|
127
|
-
if base[name].is_a?(Hash)
|
128
|
-
merge_joins(base[name], nested) unless nested.empty?
|
129
|
-
else
|
130
|
-
base[name] = nested
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
# Removes empty hashes and moves everything into arrays.
|
136
|
-
def clean_joins(joins_hash)
|
137
|
-
joins = []
|
138
|
-
joins_hash.each do |name, nested|
|
139
|
-
joins << (nested.empty? ? name : { name => clean_joins(nested) })
|
140
|
-
end
|
141
|
-
joins
|
142
|
-
end
|
143
128
|
end
|
144
129
|
end
|
145
130
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module CanCan
|
2
|
+
module ModelAdapters
|
3
|
+
module ActiveRecordAdapter
|
4
|
+
module Joins
|
5
|
+
# Returns the associations used in conditions for the :joins option of a search.
|
6
|
+
# See ModelAdditions#accessible_by
|
7
|
+
def joins
|
8
|
+
joins_hash = {}
|
9
|
+
@rules.each do |rule|
|
10
|
+
merge_joins(joins_hash, rule.associations_hash)
|
11
|
+
end
|
12
|
+
clean_joins(joins_hash) unless joins_hash.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Removes empty hashes and moves everything into arrays.
|
18
|
+
def clean_joins(joins_hash)
|
19
|
+
joins = []
|
20
|
+
joins_hash.each do |name, nested|
|
21
|
+
joins << (nested.empty? ? name : { name => clean_joins(nested) })
|
22
|
+
end
|
23
|
+
joins
|
24
|
+
end
|
25
|
+
|
26
|
+
# Takes two hashes and does a deep merge.
|
27
|
+
def merge_joins(base, add)
|
28
|
+
add.each do |name, nested|
|
29
|
+
if base[name].is_a?(Hash)
|
30
|
+
merge_joins(base[name], nested) unless nested.empty?
|
31
|
+
else
|
32
|
+
base[name] = nested
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/cancan/rule.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require_relative 'conditions_matcher.rb'
|
1
2
|
module CanCan
|
2
3
|
# This class is used internally and should only be called through Ability.
|
3
4
|
# it holds the information about a "can" call made on Ability and provides
|
4
5
|
# helpful methods to determine permission checking and conditions hash generation.
|
5
6
|
class Rule # :nodoc:
|
7
|
+
include ConditionsMatcher
|
6
8
|
attr_reader :base_behavior, :subjects, :actions, :conditions
|
7
9
|
attr_writer :expanded_actions
|
8
10
|
|
@@ -28,22 +30,6 @@ module CanCan
|
|
28
30
|
@match_all || (matches_action?(action) && matches_subject?(subject))
|
29
31
|
end
|
30
32
|
|
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
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
33
|
def only_block?
|
48
34
|
conditions_empty? && @block
|
49
35
|
end
|
@@ -52,10 +38,6 @@ module CanCan
|
|
52
38
|
@block.nil? && !conditions_empty? && !@conditions.is_a?(Hash)
|
53
39
|
end
|
54
40
|
|
55
|
-
def conditions_empty?
|
56
|
-
@conditions == {} || @conditions.nil?
|
57
|
-
end
|
58
|
-
|
59
41
|
def unmergeable?
|
60
42
|
@conditions.respond_to?(:keys) && @conditions.present? &&
|
61
43
|
(!@conditions.keys.first.is_a? Symbol)
|
@@ -83,11 +65,6 @@ module CanCan
|
|
83
65
|
|
84
66
|
private
|
85
67
|
|
86
|
-
def subject_class?(subject)
|
87
|
-
klass = (subject.is_a?(Hash) ? subject.values.first : subject).class
|
88
|
-
klass == Class || klass == Module
|
89
|
-
end
|
90
|
-
|
91
68
|
def matches_action?(action)
|
92
69
|
@expanded_actions.include?(:manage) || @expanded_actions.include?(action)
|
93
70
|
end
|
@@ -99,64 +76,8 @@ module CanCan
|
|
99
76
|
def matches_subject_class?(subject)
|
100
77
|
@subjects.any? do |sub|
|
101
78
|
sub.is_a?(Module) && (subject.is_a?(sub) ||
|
102
|
-
|
103
|
-
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
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
|
126
|
-
end
|
127
|
-
|
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
|
132
|
-
|
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
|
153
|
-
end
|
154
|
-
|
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) }
|
158
|
-
else
|
159
|
-
attribute && matches_conditions_hash?(attribute, value)
|
79
|
+
subject.class.to_s == sub.to_s ||
|
80
|
+
(subject.is_a?(Module) && subject.ancestors.include?(sub)))
|
160
81
|
end
|
161
82
|
end
|
162
83
|
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.1.
|
4
|
+
version: 2.1.1
|
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: 2017-11-
|
14
|
+
date: 2017-11-13 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: bundler
|
@@ -112,13 +112,22 @@ files:
|
|
112
112
|
- init.rb
|
113
113
|
- lib/cancan.rb
|
114
114
|
- lib/cancan/ability.rb
|
115
|
+
- lib/cancan/ability/actions.rb
|
116
|
+
- lib/cancan/ability/rules.rb
|
117
|
+
- lib/cancan/conditions_matcher.rb
|
115
118
|
- lib/cancan/controller_additions.rb
|
116
119
|
- lib/cancan/controller_resource.rb
|
120
|
+
- lib/cancan/controller_resource_builder.rb
|
121
|
+
- lib/cancan/controller_resource_finder.rb
|
122
|
+
- lib/cancan/controller_resource_loader.rb
|
123
|
+
- lib/cancan/controller_resource_name_finder.rb
|
124
|
+
- lib/cancan/controller_resource_sanitizer.rb
|
117
125
|
- lib/cancan/exceptions.rb
|
118
126
|
- lib/cancan/matchers.rb
|
119
127
|
- lib/cancan/model_adapters/abstract_adapter.rb
|
120
128
|
- lib/cancan/model_adapters/active_record_4_adapter.rb
|
121
129
|
- lib/cancan/model_adapters/active_record_adapter.rb
|
130
|
+
- lib/cancan/model_adapters/can_can/model_adapters/active_record_adapter/joins.rb
|
122
131
|
- lib/cancan/model_adapters/default_adapter.rb
|
123
132
|
- lib/cancan/model_additions.rb
|
124
133
|
- lib/cancan/rule.rb
|