mini-lite-tool 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.
- checksums.yaml +7 -0
- data/cancancan-3.6.1/cancancan.gemspec +29 -0
- data/cancancan-3.6.1/init.rb +3 -0
- data/cancancan-3.6.1/lib/cancan/ability/actions.rb +93 -0
- data/cancancan-3.6.1/lib/cancan/ability/rules.rb +96 -0
- data/cancancan-3.6.1/lib/cancan/ability/strong_parameter_support.rb +41 -0
- data/cancancan-3.6.1/lib/cancan/ability.rb +312 -0
- data/cancancan-3.6.1/lib/cancan/class_matcher.rb +30 -0
- data/cancancan-3.6.1/lib/cancan/conditions_matcher.rb +147 -0
- data/cancancan-3.6.1/lib/cancan/config.rb +101 -0
- data/cancancan-3.6.1/lib/cancan/controller_additions.rb +399 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource.rb +141 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource_builder.rb +26 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource_finder.rb +42 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource_loader.rb +120 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource_name_finder.rb +23 -0
- data/cancancan-3.6.1/lib/cancan/controller_resource_sanitizer.rb +32 -0
- data/cancancan-3.6.1/lib/cancan/exceptions.rb +70 -0
- data/cancancan-3.6.1/lib/cancan/matchers.rb +56 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/abstract_adapter.rb +77 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_4_adapter.rb +62 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_5_adapter.rb +61 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/active_record_adapter.rb +229 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/conditions_extractor.rb +75 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/conditions_normalizer.rb +49 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/default_adapter.rb +9 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/sti_normalizer.rb +47 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/base.rb +40 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/joined_alias_each_rule_as_exists_subquery.rb +93 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/joined_alias_exists_subquery.rb +31 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/left_join.rb +11 -0
- data/cancancan-3.6.1/lib/cancan/model_adapters/strategies/subquery.rb +18 -0
- data/cancancan-3.6.1/lib/cancan/model_additions.rb +34 -0
- data/cancancan-3.6.1/lib/cancan/parameter_validators.rb +9 -0
- data/cancancan-3.6.1/lib/cancan/relevant.rb +29 -0
- data/cancancan-3.6.1/lib/cancan/rule.rb +140 -0
- data/cancancan-3.6.1/lib/cancan/rules_compressor.rb +41 -0
- data/cancancan-3.6.1/lib/cancan/sti_detector.rb +12 -0
- data/cancancan-3.6.1/lib/cancan/unauthorized_message_resolver.rb +24 -0
- data/cancancan-3.6.1/lib/cancan/version.rb +5 -0
- data/cancancan-3.6.1/lib/cancan.rb +29 -0
- data/cancancan-3.6.1/lib/cancancan.rb +6 -0
- data/cancancan-3.6.1/lib/generators/cancan/ability/USAGE +4 -0
- data/cancancan-3.6.1/lib/generators/cancan/ability/ability_generator.rb +13 -0
- data/cancancan-3.6.1/lib/generators/cancan/ability/templates/ability.rb +32 -0
- data/mini-lite-tool.gemspec +11 -0
- metadata +85 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'controller_resource_loader.rb'
|
|
4
|
+
module CanCan
|
|
5
|
+
# Handle the load and authorization controller logic
|
|
6
|
+
# so we don't clutter up all controllers with non-interface methods.
|
|
7
|
+
# This class is used internally, so you do not need to call methods directly on it.
|
|
8
|
+
class ControllerResource # :nodoc:
|
|
9
|
+
include ControllerResourceLoader
|
|
10
|
+
|
|
11
|
+
def self.add_before_action(controller_class, method, *args)
|
|
12
|
+
options = args.extract_options!
|
|
13
|
+
resource_name = args.first
|
|
14
|
+
before_action_method = before_callback_name(options)
|
|
15
|
+
controller_class.send(before_action_method, options.slice(:only, :except, :if, :unless)) do |controller|
|
|
16
|
+
controller.class.cancan_resource_class
|
|
17
|
+
.new(controller, resource_name, options.except(:only, :except, :if, :unless)).send(method)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.before_callback_name(options)
|
|
22
|
+
options.delete(:prepend) ? :prepend_before_action : :before_action
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize(controller, *args)
|
|
26
|
+
@controller = controller
|
|
27
|
+
@params = controller.params
|
|
28
|
+
@options = args.extract_options!
|
|
29
|
+
@name = args.first
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def load_and_authorize_resource
|
|
33
|
+
load_resource
|
|
34
|
+
authorize_resource
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def authorize_resource
|
|
38
|
+
return if skip?(:authorize)
|
|
39
|
+
|
|
40
|
+
@controller.authorize!(authorization_action, resource_instance || resource_class_with_parent)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def parent?
|
|
44
|
+
@options.key?(:parent) ? @options[:parent] : @name && @name != name_from_controller.to_sym
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def skip?(behavior)
|
|
48
|
+
return false unless (options = @controller.class.cancan_skipper[behavior][@name])
|
|
49
|
+
|
|
50
|
+
options == {} ||
|
|
51
|
+
options[:except] && !action_exists_in?(options[:except]) ||
|
|
52
|
+
action_exists_in?(options[:only])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
protected
|
|
56
|
+
|
|
57
|
+
# Returns the class used for this resource. This can be overridden by the :class option.
|
|
58
|
+
# If +false+ is passed in it will use the resource name as a symbol in which case it should
|
|
59
|
+
# only be used for authorization, not loading since there's no class to load through.
|
|
60
|
+
def resource_class
|
|
61
|
+
case @options[:class]
|
|
62
|
+
when false
|
|
63
|
+
name.to_sym
|
|
64
|
+
when nil
|
|
65
|
+
namespaced_name.to_s.camelize.constantize
|
|
66
|
+
when String
|
|
67
|
+
@options[:class].constantize
|
|
68
|
+
else
|
|
69
|
+
@options[:class]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def load_instance?
|
|
74
|
+
parent? || member_action?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def load_collection?
|
|
78
|
+
resource_base.respond_to?(:accessible_by) && !current_ability.has_block?(authorization_action, resource_class)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def member_action?
|
|
82
|
+
new_actions.include?(@params[:action].to_sym) || @options[:singleton] ||
|
|
83
|
+
((@params[:id] || @params[@options[:id_param]]) &&
|
|
84
|
+
!collection_actions.include?(@params[:action].to_sym))
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def resource_class_with_parent
|
|
88
|
+
parent_resource ? { parent_resource => resource_class } : resource_class
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def resource_instance=(instance)
|
|
92
|
+
@controller.instance_variable_set("@#{instance_name}", instance)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def resource_instance
|
|
96
|
+
return unless load_instance? && @controller.instance_variable_defined?("@#{instance_name}")
|
|
97
|
+
|
|
98
|
+
@controller.instance_variable_get("@#{instance_name}")
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def collection_instance=(instance)
|
|
102
|
+
@controller.instance_variable_set("@#{instance_name.to_s.pluralize}", instance)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def collection_instance
|
|
106
|
+
return unless @controller.instance_variable_defined?("@#{instance_name.to_s.pluralize}")
|
|
107
|
+
|
|
108
|
+
@controller.instance_variable_get("@#{instance_name.to_s.pluralize}")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def parameters_require_sanitizing?
|
|
112
|
+
save_actions.include?(@params[:action].to_sym) || resource_params_by_namespaced_name.present?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def instance_name
|
|
116
|
+
@options[:instance_name] || name
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def collection_actions
|
|
120
|
+
[:index] + Array(@options[:collection])
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def save_actions
|
|
124
|
+
%i[create update]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
def action_exists_in?(options)
|
|
130
|
+
Array(options).include?(@params[:action].to_sym)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def adapter
|
|
134
|
+
ModelAdapters::AbstractAdapter.adapter_class(resource_class)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def current_ability
|
|
138
|
+
@controller.send(:current_ability)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ControllerResourceBuilder
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def build_resource
|
|
8
|
+
resource = resource_base.new(resource_params || {})
|
|
9
|
+
assign_attributes(resource)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def assign_attributes(resource)
|
|
13
|
+
resource.send("#{parent_name}=", parent_resource) if @options[:singleton] && parent_resource
|
|
14
|
+
initial_attributes.each do |attr_name, value|
|
|
15
|
+
resource.send("#{attr_name}=", value)
|
|
16
|
+
end
|
|
17
|
+
resource
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initial_attributes
|
|
21
|
+
current_ability.attributes_for(@params[:action].to_sym, resource_class).delete_if do |key, _value|
|
|
22
|
+
resource_params && resource_params.include?(key)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ControllerResourceFinder
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def find_resource
|
|
8
|
+
if @options[:singleton] && parent_resource.respond_to?(name)
|
|
9
|
+
parent_resource.send(name)
|
|
10
|
+
elsif @options[:find_by]
|
|
11
|
+
find_resource_using_find_by
|
|
12
|
+
else
|
|
13
|
+
adapter.find(resource_base, id_param)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def find_resource_using_find_by
|
|
18
|
+
find_by_dynamic_finder || find_by_find_by_finder || resource_base.send(@options[:find_by], id_param)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def find_by_dynamic_finder
|
|
22
|
+
method_name = "find_by_#{@options[:find_by]}!"
|
|
23
|
+
resource_base.send(method_name, id_param) if resource_base.respond_to? method_name
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def find_by_find_by_finder
|
|
27
|
+
resource_base.find_by(@options[:find_by].to_sym => id_param) if resource_base.respond_to? :find_by
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def id_param
|
|
31
|
+
@params[id_param_key].to_s if @params[id_param_key].present?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def id_param_key
|
|
35
|
+
if @options[:id_param]
|
|
36
|
+
@options[:id_param]
|
|
37
|
+
else
|
|
38
|
+
parent? ? :"#{name}_id" : :id
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'controller_resource_finder.rb'
|
|
4
|
+
require_relative 'controller_resource_name_finder.rb'
|
|
5
|
+
require_relative 'controller_resource_builder.rb'
|
|
6
|
+
require_relative 'controller_resource_sanitizer.rb'
|
|
7
|
+
module CanCan
|
|
8
|
+
module ControllerResourceLoader
|
|
9
|
+
include CanCan::ControllerResourceNameFinder
|
|
10
|
+
include CanCan::ControllerResourceFinder
|
|
11
|
+
include CanCan::ControllerResourceBuilder
|
|
12
|
+
include CanCan::ControllerResourceSanitizer
|
|
13
|
+
|
|
14
|
+
def load_resource
|
|
15
|
+
return if skip?(:load)
|
|
16
|
+
|
|
17
|
+
if load_instance?
|
|
18
|
+
self.resource_instance ||= load_resource_instance
|
|
19
|
+
elsif load_collection?
|
|
20
|
+
self.collection_instance ||= load_collection
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
protected
|
|
25
|
+
|
|
26
|
+
def new_actions
|
|
27
|
+
%i[new create] + Array(@options[:new])
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def resource_params_by_key(key)
|
|
31
|
+
return unless @options[key] && @params.key?(extract_key(@options[key]))
|
|
32
|
+
|
|
33
|
+
@params[extract_key(@options[key])]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def resource_params_by_namespaced_name
|
|
37
|
+
resource_params_by_key(:instance_name) || resource_params_by_key(:class) || (
|
|
38
|
+
params = @params[extract_key(namespaced_name)]
|
|
39
|
+
params.respond_to?(:to_h) ? params : nil)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def resource_params
|
|
43
|
+
if parameters_require_sanitizing? && params_method.present?
|
|
44
|
+
sanitize_parameters
|
|
45
|
+
else
|
|
46
|
+
resource_params_by_namespaced_name
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def fetch_parent(name)
|
|
51
|
+
if @controller.instance_variable_defined? "@#{name}"
|
|
52
|
+
@controller.instance_variable_get("@#{name}")
|
|
53
|
+
elsif @controller.respond_to?(name, true)
|
|
54
|
+
@controller.send(name)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# The object to load this resource through.
|
|
59
|
+
def parent_resource
|
|
60
|
+
parent_name && fetch_parent(parent_name)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def parent_name
|
|
64
|
+
@options[:through] && [@options[:through]].flatten.detect { |i| fetch_parent(i) }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def resource_base_through_parent_resource
|
|
68
|
+
if @options[:singleton]
|
|
69
|
+
resource_class
|
|
70
|
+
else
|
|
71
|
+
parent_resource.send(@options[:through_association] || name.to_s.pluralize)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def resource_base_through
|
|
76
|
+
if parent_resource
|
|
77
|
+
resource_base_through_parent_resource
|
|
78
|
+
elsif @options[:shallow]
|
|
79
|
+
resource_class
|
|
80
|
+
else
|
|
81
|
+
# maybe this should be a record not found error instead?
|
|
82
|
+
raise AccessDenied.new(nil, authorization_action, resource_class)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# The object that methods (such as "find", "new" or "build") are called on.
|
|
87
|
+
# If the :through option is passed it will go through an association on that instance.
|
|
88
|
+
# If the :shallow option is passed it will use the resource_class if there's no parent
|
|
89
|
+
# If the :singleton option is passed it won't use the association because it needs to be handled later.
|
|
90
|
+
def resource_base
|
|
91
|
+
@options[:through] ? resource_base_through : resource_class
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def parent_authorization_action
|
|
95
|
+
@options[:parent_action] || :show
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def authorization_action
|
|
99
|
+
parent? ? parent_authorization_action : @params[:action].to_sym
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load_collection
|
|
103
|
+
resource_base.accessible_by(current_ability, authorization_action)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def load_resource_instance
|
|
107
|
+
if !parent? && new_actions.include?(@params[:action].to_sym)
|
|
108
|
+
build_resource
|
|
109
|
+
elsif id_param || @options[:singleton]
|
|
110
|
+
find_resource
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def extract_key(value)
|
|
117
|
+
value.to_s.underscore.tr('/', '_')
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ControllerResourceNameFinder
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def name_from_controller
|
|
8
|
+
@params[:controller].split('/').last.singularize
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def namespaced_name
|
|
12
|
+
[namespace, name].join('/').singularize.camelize.safe_constantize || name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name
|
|
16
|
+
@name || name_from_controller
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def namespace
|
|
20
|
+
@params[:controller].split('/')[0..-2]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ControllerResourceSanitizer
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def sanitize_parameters
|
|
8
|
+
case params_method
|
|
9
|
+
when Symbol
|
|
10
|
+
@controller.send(params_method)
|
|
11
|
+
when String
|
|
12
|
+
@controller.instance_eval(params_method)
|
|
13
|
+
when Proc
|
|
14
|
+
params_method.call(@controller)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def params_methods
|
|
19
|
+
methods = ["#{@params[:action]}_params".to_sym, "#{name}_params".to_sym, :resource_params]
|
|
20
|
+
methods.unshift(@options[:param_method]) if @options[:param_method].present?
|
|
21
|
+
methods
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def params_method
|
|
25
|
+
params_methods.each do |method|
|
|
26
|
+
return method if (method.is_a?(Symbol) && @controller.respond_to?(method, true)) ||
|
|
27
|
+
method.is_a?(String) || method.is_a?(Proc)
|
|
28
|
+
end
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
# A general CanCan exception
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
# Raised when behavior is not implemented, usually used in an abstract class.
|
|
8
|
+
class NotImplemented < Error; end
|
|
9
|
+
|
|
10
|
+
# Raised when removed code is called, an alternative solution is provided in message.
|
|
11
|
+
class ImplementationRemoved < Error; end
|
|
12
|
+
|
|
13
|
+
# Raised when using check_authorization without calling authorize!
|
|
14
|
+
class AuthorizationNotPerformed < Error; end
|
|
15
|
+
|
|
16
|
+
# Raised when a rule is created with both a block and a hash of conditions
|
|
17
|
+
class BlockAndConditionsError < Error; end
|
|
18
|
+
|
|
19
|
+
# Raised when an unexpected argument is passed as an attribute
|
|
20
|
+
class AttributeArgumentError < Error; end
|
|
21
|
+
|
|
22
|
+
# Raised when using a wrong association name
|
|
23
|
+
class WrongAssociationName < Error; end
|
|
24
|
+
|
|
25
|
+
# This error is raised when a user isn't allowed to access a given controller action.
|
|
26
|
+
# This usually happens within a call to ControllerAdditions#authorize! but can be
|
|
27
|
+
# raised manually.
|
|
28
|
+
#
|
|
29
|
+
# raise CanCan::AccessDenied.new("Not authorized!", :read, Article)
|
|
30
|
+
#
|
|
31
|
+
# The passed message, action, and subject are optional and can later be retrieved when
|
|
32
|
+
# rescuing from the exception.
|
|
33
|
+
#
|
|
34
|
+
# exception.message # => "Not authorized!"
|
|
35
|
+
# exception.action # => :read
|
|
36
|
+
# exception.subject # => Article
|
|
37
|
+
#
|
|
38
|
+
# If the message is not specified (or is nil) it will default to "You are not authorized
|
|
39
|
+
# to access this page." This default can be overridden by setting default_message.
|
|
40
|
+
#
|
|
41
|
+
# exception.default_message = "Default error message"
|
|
42
|
+
# exception.message # => "Default error message"
|
|
43
|
+
#
|
|
44
|
+
# See ControllerAdditions#authorize! for more information on rescuing from this exception
|
|
45
|
+
# and customizing the message using I18n.
|
|
46
|
+
class AccessDenied < Error
|
|
47
|
+
attr_reader :action, :subject, :conditions
|
|
48
|
+
attr_writer :default_message
|
|
49
|
+
|
|
50
|
+
def initialize(message = nil, action = nil, subject = nil, conditions = nil)
|
|
51
|
+
@message = message
|
|
52
|
+
@action = action
|
|
53
|
+
@subject = subject
|
|
54
|
+
@conditions = conditions
|
|
55
|
+
@default_message = I18n.t(:"unauthorized.default", default: 'You are not authorized to access this page.')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def to_s
|
|
59
|
+
@message || @default_message
|
|
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
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
rspec_module = defined?(RSpec::Core) ? 'RSpec' : 'Spec' # RSpec 1 compatability
|
|
4
|
+
|
|
5
|
+
if rspec_module == 'RSpec'
|
|
6
|
+
require 'rspec/core'
|
|
7
|
+
require 'rspec/expectations'
|
|
8
|
+
else
|
|
9
|
+
ActiveSupport::Deprecation.warn('RSpec < 3 will not be supported in the CanCanCan >= 2.0.0')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Kernel.const_get(rspec_module)::Matchers.define :be_able_to do |*args|
|
|
13
|
+
match do |ability|
|
|
14
|
+
actions = args.first
|
|
15
|
+
if actions.is_a? Array
|
|
16
|
+
if actions.empty?
|
|
17
|
+
false
|
|
18
|
+
else
|
|
19
|
+
actions.all? { |action| ability.can?(action, *args[1..-1]) }
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
ability.can?(*args)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Check that RSpec is < 2.99
|
|
27
|
+
if !respond_to?(:failure_message) && respond_to?(:failure_message_for_should)
|
|
28
|
+
alias_method :failure_message, :failure_message_for_should
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if !respond_to?(:failure_message_when_negated) && respond_to?(:failure_message_for_should_not)
|
|
32
|
+
alias_method :failure_message_when_negated, :failure_message_for_should_not
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
failure_message do
|
|
36
|
+
resource = args[1]
|
|
37
|
+
if resource.instance_of?(Class)
|
|
38
|
+
"expected to be able to #{args.map(&:to_s).join(' ')}"
|
|
39
|
+
else
|
|
40
|
+
"expected to be able to #{args.map(&:inspect).join(' ')}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
failure_message_when_negated do
|
|
45
|
+
resource = args[1]
|
|
46
|
+
if resource.instance_of?(Class)
|
|
47
|
+
"expected not to be able to #{args.map(&:to_s).join(' ')}"
|
|
48
|
+
else
|
|
49
|
+
"expected not to be able to #{args.map(&:inspect).join(' ')}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
description do
|
|
54
|
+
"be able to #{args.map(&:to_s).join(' ')}"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ModelAdapters
|
|
5
|
+
class AbstractAdapter
|
|
6
|
+
attr_reader :model_class
|
|
7
|
+
|
|
8
|
+
def self.inherited(subclass)
|
|
9
|
+
@subclasses ||= []
|
|
10
|
+
@subclasses.insert(0, subclass)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.adapter_class(model_class)
|
|
14
|
+
@subclasses.detect { |subclass| subclass.for_class?(model_class) } || DefaultAdapter
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Used to determine if the given adapter should be used for the passed in class.
|
|
18
|
+
def self.for_class?(_member_class)
|
|
19
|
+
false # override in subclass
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Override if you need custom find behavior
|
|
23
|
+
def self.find(model_class, id)
|
|
24
|
+
model_class.find(id)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Used to determine if this model adapter will override the matching behavior for a hash of conditions.
|
|
28
|
+
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
|
29
|
+
def self.override_conditions_hash_matching?(_subject, _conditions)
|
|
30
|
+
false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Override if override_conditions_hash_matching? returns true
|
|
34
|
+
def self.matches_conditions_hash?(_subject, _conditions)
|
|
35
|
+
raise NotImplemented, 'This model adapter does not support matching on a conditions hash.'
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Override if parent condition could be under a different key in conditions
|
|
39
|
+
def self.parent_condition_name(parent, _child)
|
|
40
|
+
parent.class.name.downcase.to_sym
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Used above override_conditions_hash_matching to determine if this model adapter will override the
|
|
44
|
+
# matching behavior for nested subject.
|
|
45
|
+
# If this returns true then nested_subject_matches_conditions? will be called.
|
|
46
|
+
def self.override_nested_subject_conditions_matching?(_parent, _child, _all_conditions)
|
|
47
|
+
false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Override if override_nested_subject_conditions_matching? returns true
|
|
51
|
+
def self.nested_subject_matches_conditions?(_parent, _child, _all_conditions)
|
|
52
|
+
raise NotImplemented, 'This model adapter does not support matching on a nested subject.'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Used to determine if this model adapter will override the matching behavior for a specific condition.
|
|
56
|
+
# If this returns true then matches_condition? will be called. See Rule#matches_conditions_hash
|
|
57
|
+
def self.override_condition_matching?(_subject, _name, _value)
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Override if override_condition_matching? returns true
|
|
62
|
+
def self.matches_condition?(_subject, _name, _value)
|
|
63
|
+
raise NotImplemented, 'This model adapter does not support matching on a specific condition.'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(model_class, rules)
|
|
67
|
+
@model_class = model_class
|
|
68
|
+
@rules = rules
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def database_records
|
|
72
|
+
# This should be overridden in a subclass to return records which match @rules
|
|
73
|
+
raise NotImplemented, 'This model adapter does not support fetching records from the database.'
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CanCan
|
|
4
|
+
module ModelAdapters
|
|
5
|
+
class ActiveRecord4Adapter < ActiveRecordAdapter
|
|
6
|
+
AbstractAdapter.inherited(self)
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def for_class?(model_class)
|
|
10
|
+
version_lower?('5.0.0') && model_class <= ActiveRecord::Base
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def override_condition_matching?(subject, name, _value)
|
|
14
|
+
subject.class.defined_enums.include?(name.to_s)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def matches_condition?(subject, name, value)
|
|
18
|
+
# Get the mapping from enum strings to values.
|
|
19
|
+
enum = subject.class.send(name.to_s.pluralize)
|
|
20
|
+
# Get the value of the attribute as an integer.
|
|
21
|
+
attribute = enum[subject.send(name)]
|
|
22
|
+
# Check to see if the value matches the condition.
|
|
23
|
+
if value.is_a?(Enumerable)
|
|
24
|
+
value.include? attribute
|
|
25
|
+
else
|
|
26
|
+
attribute == value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# As of rails 4, `includes()` no longer causes active record to
|
|
34
|
+
# look inside the where clause to decide to outer join tables
|
|
35
|
+
# you're using in the where. Instead, `references()` is required
|
|
36
|
+
# in addition to `includes()` to force the outer join.
|
|
37
|
+
def build_joins_relation(relation, *_where_conditions)
|
|
38
|
+
relation.includes(joins).references(joins)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Rails 4.2 deprecates `sanitize_sql_hash_for_conditions`
|
|
42
|
+
def sanitize_sql(conditions)
|
|
43
|
+
if self.class.version_greater_or_equal?('4.2.0') && conditions.is_a?(Hash)
|
|
44
|
+
sanitize_sql_activerecord4(conditions)
|
|
45
|
+
else
|
|
46
|
+
@model_class.send(:sanitize_sql, conditions)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def sanitize_sql_activerecord4(conditions)
|
|
51
|
+
table = Arel::Table.new(@model_class.send(:table_name))
|
|
52
|
+
|
|
53
|
+
conditions = ActiveRecord::PredicateBuilder.resolve_column_aliases @model_class, conditions
|
|
54
|
+
conditions = @model_class.send(:expand_hash_conditions_for_aggregates, conditions)
|
|
55
|
+
|
|
56
|
+
ActiveRecord::PredicateBuilder.build_from_hash(@model_class, conditions, table).map do |b|
|
|
57
|
+
@model_class.send(:connection).visitor.compile b
|
|
58
|
+
end.join(' AND ')
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|