action_policy 0.0.1 → 0.1.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 +5 -5
- data/.rubocop.yml +85 -0
- data/.travis.yml +25 -2
- data/CHANGELOG.md +7 -0
- data/Gemfile +12 -3
- data/README.md +71 -12
- data/Rakefile +9 -1
- data/action_policy.gemspec +11 -5
- data/docs/.nojekyll +0 -0
- data/docs/CNAME +1 -0
- data/docs/README.md +46 -0
- data/docs/_sidebar.md +19 -0
- data/docs/aliases.md +54 -0
- data/docs/assets/docsify.min.js +1 -0
- data/docs/assets/fonts/FiraCode-Medium.woff +0 -0
- data/docs/assets/fonts/FiraCode-Regular.woff +0 -0
- data/docs/assets/images/cache.png +0 -0
- data/docs/assets/images/cache.svg +70 -0
- data/docs/assets/images/layer.png +0 -0
- data/docs/assets/images/layer.svg +92 -0
- data/docs/assets/prism-ruby.min.js +1 -0
- data/docs/assets/styles.css +317 -0
- data/docs/assets/vue.min.css +1 -0
- data/docs/authorization_context.md +33 -0
- data/docs/caching.md +262 -0
- data/docs/custom_lookup_chain.md +48 -0
- data/docs/custom_policy.md +51 -0
- data/docs/favicon.ico +0 -0
- data/docs/i18n.md +3 -0
- data/docs/index.html +25 -0
- data/docs/instrumentation.md +3 -0
- data/docs/lookup_chain.md +16 -0
- data/docs/namespaces.md +69 -0
- data/docs/non_rails.md +29 -0
- data/docs/pre_checks.md +57 -0
- data/docs/quick_start.md +102 -0
- data/docs/rails.md +110 -0
- data/docs/reasons.md +67 -0
- data/docs/testing.md +116 -0
- data/docs/writing_policies.md +55 -0
- data/gemfiles/jruby.gemfile +5 -0
- data/gemfiles/rails42.gemfile +5 -0
- data/gemfiles/railsmaster.gemfile +6 -0
- data/lib/action_policy.rb +34 -2
- data/lib/action_policy/authorizer.rb +28 -0
- data/lib/action_policy/base.rb +24 -0
- data/lib/action_policy/behaviour.rb +94 -0
- data/lib/action_policy/behaviours/memoized.rb +56 -0
- data/lib/action_policy/behaviours/namespaced.rb +80 -0
- data/lib/action_policy/behaviours/policy_for.rb +23 -0
- data/lib/action_policy/behaviours/thread_memoized.rb +54 -0
- data/lib/action_policy/ext/module_namespace.rb +21 -0
- data/lib/action_policy/ext/policy_cache_key.rb +67 -0
- data/lib/action_policy/ext/string_constantize.rb +23 -0
- data/lib/action_policy/lookup_chain.rb +84 -0
- data/lib/action_policy/policy/aliases.rb +69 -0
- data/lib/action_policy/policy/authorization.rb +91 -0
- data/lib/action_policy/policy/cache.rb +74 -0
- data/lib/action_policy/policy/cached_apply.rb +28 -0
- data/lib/action_policy/policy/core.rb +64 -0
- data/lib/action_policy/policy/defaults.rb +37 -0
- data/lib/action_policy/policy/pre_check.rb +210 -0
- data/lib/action_policy/policy/reasons.rb +109 -0
- data/lib/action_policy/rails/channel.rb +15 -0
- data/lib/action_policy/rails/controller.rb +90 -0
- data/lib/action_policy/railtie.rb +74 -0
- data/lib/action_policy/rspec.rb +3 -0
- data/lib/action_policy/rspec/be_authorized_to.rb +93 -0
- data/lib/action_policy/rspec/pundit_syntax.rb +48 -0
- data/lib/action_policy/test_helper.rb +46 -0
- data/lib/action_policy/testing.rb +64 -0
- data/lib/action_policy/version.rb +3 -1
- metadata +115 -9
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "action_policy/behaviours/policy_for"
|
4
|
+
|
5
|
+
module ActionPolicy
|
6
|
+
# Raised when `resolve_rule` failed to find an approriate
|
7
|
+
# policy rule method for the activity
|
8
|
+
class UnknownRule < Error
|
9
|
+
attr_reader :policy, :rule, :message
|
10
|
+
|
11
|
+
def initialize(policy, rule)
|
12
|
+
@policy = policy.class
|
13
|
+
@rule = rule
|
14
|
+
@message = "Couldn't find rule '#{@rule}' for #{@policy}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Policy
|
19
|
+
# Core policy API
|
20
|
+
module Core
|
21
|
+
include ActionPolicy::Behaviours::PolicyFor
|
22
|
+
|
23
|
+
attr_reader :record
|
24
|
+
|
25
|
+
def initialize(record = nil)
|
26
|
+
@record = record
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns a result of applying the specified rule.
|
30
|
+
# Unlike simply calling a predicate rule (`policy.manage?`),
|
31
|
+
# `apply` also calls pre-checks.
|
32
|
+
def apply(rule)
|
33
|
+
public_send(rule)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Returns a result of applying the specified rule to the specified record.
|
37
|
+
# Under the hood a policy class for record is resolved
|
38
|
+
# (unless it's explicitly set through `with` option).
|
39
|
+
#
|
40
|
+
# If record is `nil` then we uses the current policy.
|
41
|
+
def allowed_to?(rule, record = :__undef__, **options)
|
42
|
+
policy =
|
43
|
+
if record == :__undef__
|
44
|
+
self
|
45
|
+
else
|
46
|
+
policy_for(record: record, **options)
|
47
|
+
end
|
48
|
+
|
49
|
+
policy.apply(rule)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a rule name (policy method name) for activity.
|
53
|
+
#
|
54
|
+
# By default, rule name is equal to activity name.
|
55
|
+
#
|
56
|
+
# Raises ActionPolicy::UknownRule when rule is not found in policy.
|
57
|
+
def resolve_rule(activity)
|
58
|
+
raise UnknownRule.new(self, activity) unless
|
59
|
+
respond_to?(activity)
|
60
|
+
activity
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionPolicy
|
4
|
+
module Policy
|
5
|
+
# Create default rules and aliases:
|
6
|
+
# - `index?` (=`false`)
|
7
|
+
# - `create?` (=`false`)
|
8
|
+
# - `new?` as an alias for `create?`
|
9
|
+
# - `manage?` as a fallback for all unspecified rules (default rule)
|
10
|
+
module Defaults
|
11
|
+
def self.included(base)
|
12
|
+
raise "Aliases support is required for defaults" unless
|
13
|
+
base.ancestors.include?(Aliases)
|
14
|
+
|
15
|
+
base.default_rule :manage?
|
16
|
+
base.alias_rule :new?, to: :create?
|
17
|
+
|
18
|
+
raise "Verification context support is required for defaults" unless
|
19
|
+
base.ancestors.include?(Aliases)
|
20
|
+
|
21
|
+
base.authorize :user
|
22
|
+
end
|
23
|
+
|
24
|
+
def index?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def create?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def manage?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionPolicy
|
4
|
+
module Policy
|
5
|
+
# Adds callback-style checks to policies to
|
6
|
+
# extract common checks from rules.
|
7
|
+
#
|
8
|
+
# class ApplicationPolicy < ActionPolicy::Base
|
9
|
+
# authorize :user
|
10
|
+
# pre_check :allow_admins
|
11
|
+
#
|
12
|
+
# private
|
13
|
+
# # Allow every action for admins
|
14
|
+
# def allow_admins
|
15
|
+
# allow! if user.admin?
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# You can specify conditional pre-checks (through `except` / `only`) options
|
20
|
+
# and skip already defined pre-checks if necessary.
|
21
|
+
#
|
22
|
+
# class UserPolicy < ApplicationPolicy
|
23
|
+
# skip_pre_check :allow_admins, only: :destroy?
|
24
|
+
#
|
25
|
+
# def destroy?
|
26
|
+
# user.admin? && !record.admin?
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
module PreCheck
|
30
|
+
# Single pre-check instance.
|
31
|
+
#
|
32
|
+
# Implements filtering logic.
|
33
|
+
class Check
|
34
|
+
# Wrapper over check result
|
35
|
+
class Result
|
36
|
+
DENY = :__deny__
|
37
|
+
ALLOW = :__allow__
|
38
|
+
|
39
|
+
KINDS = {
|
40
|
+
DENY => false,
|
41
|
+
ALLOW => true
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
attr_reader :fulfilled
|
45
|
+
|
46
|
+
def initialize(val)
|
47
|
+
@fulfilled = KINDS.keys.include?(val)
|
48
|
+
@value = val
|
49
|
+
end
|
50
|
+
|
51
|
+
alias fulfilled? fulfilled
|
52
|
+
|
53
|
+
def denied?
|
54
|
+
@value == DENY
|
55
|
+
end
|
56
|
+
|
57
|
+
def value
|
58
|
+
KINDS.fetch(@value)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :name, :policy_class
|
63
|
+
|
64
|
+
def initialize(policy, name, except: nil, only: nil)
|
65
|
+
if !except.nil? && !only.nil?
|
66
|
+
raise ArgumentError,
|
67
|
+
"Only one of `except` and `only` may be specified for pre-check"
|
68
|
+
end
|
69
|
+
|
70
|
+
@policy_class = policy
|
71
|
+
@name = name
|
72
|
+
@blacklist = Array(except) unless except.nil?
|
73
|
+
@whitelist = Array(only) unless only.nil?
|
74
|
+
|
75
|
+
rebuild_filter
|
76
|
+
end
|
77
|
+
|
78
|
+
def applicable?(rule)
|
79
|
+
return true if filter.nil?
|
80
|
+
filter.call(rule)
|
81
|
+
end
|
82
|
+
|
83
|
+
def call(policy)
|
84
|
+
Result.new(policy.send(name)).tap do |res|
|
85
|
+
# add denial reason if Reasons included
|
86
|
+
policy.reasons.add(policy_class, name) if
|
87
|
+
res.denied? && policy.respond_to?(:reasons)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# rubocop: disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
92
|
+
# rubocop: disable Metrics/PerceivedComplexity, Metrics/MethodLength
|
93
|
+
def skip!(except: nil, only: nil)
|
94
|
+
if !except.nil? && !only.nil?
|
95
|
+
raise ArgumentError,
|
96
|
+
"Only one of `except` and `only` may be specified when skipping pre-check"
|
97
|
+
end
|
98
|
+
|
99
|
+
if except.nil? && only.nil?
|
100
|
+
raise ArgumentError,
|
101
|
+
"At least one of `except` and `only` must be specified when skipping pre-check"
|
102
|
+
end
|
103
|
+
|
104
|
+
if except
|
105
|
+
@whitelist = Array(except)
|
106
|
+
@whitelist -= blacklist if blacklist
|
107
|
+
@blacklist = nil
|
108
|
+
else
|
109
|
+
# only
|
110
|
+
@blacklist += Array(only) if blacklist
|
111
|
+
@whitelist -= Array(only) if whitelist
|
112
|
+
@blacklist = Array(only) if filter.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
rebuild_filter
|
116
|
+
end
|
117
|
+
# rubocop: enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
118
|
+
# rubocop: enable Metrics/PerceivedComplexity, Metrics/MethodLength
|
119
|
+
|
120
|
+
def dup
|
121
|
+
self.class.new(policy_class, name, except: blacklist&.dup, only: whitelist&.dup)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
attr_reader :whitelist, :blacklist, :filter
|
127
|
+
|
128
|
+
def rebuild_filter
|
129
|
+
@filter =
|
130
|
+
if whitelist
|
131
|
+
proc { |rule| whitelist.include?(rule) }
|
132
|
+
elsif blacklist
|
133
|
+
proc { |rule| !blacklist.include?(rule) }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class << self
|
139
|
+
def prepended(base)
|
140
|
+
base.extend ClassMethods
|
141
|
+
base.prepend InstanceMethods
|
142
|
+
end
|
143
|
+
|
144
|
+
alias included prepended
|
145
|
+
end
|
146
|
+
|
147
|
+
def run_pre_checks(rule)
|
148
|
+
self.class.pre_checks.each do |check|
|
149
|
+
next unless check.applicable?(rule)
|
150
|
+
res = check.call(self)
|
151
|
+
return res.value if res.fulfilled?
|
152
|
+
end
|
153
|
+
|
154
|
+
yield if block_given?
|
155
|
+
end
|
156
|
+
|
157
|
+
def deny!
|
158
|
+
Check::Result::DENY
|
159
|
+
end
|
160
|
+
|
161
|
+
def allow!
|
162
|
+
Check::Result::ALLOW
|
163
|
+
end
|
164
|
+
|
165
|
+
module InstanceMethods # :nodoc:
|
166
|
+
def apply(rule)
|
167
|
+
run_pre_checks(rule) { super }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
module ClassMethods # :nodoc:
|
172
|
+
def pre_check(*names, **options)
|
173
|
+
names.each do |name|
|
174
|
+
# do not allow pre-check override
|
175
|
+
check = pre_checks.find { |c| c.name == name }
|
176
|
+
raise "Pre-check already defined: #{name}" unless check.nil?
|
177
|
+
|
178
|
+
pre_checks << Check.new(self, name, **options)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# rubocop: disable Metrics/AbcSize
|
183
|
+
def skip_pre_check(*names, **options)
|
184
|
+
names.each do |name|
|
185
|
+
check = pre_checks.find { |c| c.name == name }
|
186
|
+
raise "Pre-check not found: #{name}" if check.nil?
|
187
|
+
|
188
|
+
# when no options provided we remove this check completely
|
189
|
+
next pre_checks.delete(check) if options.empty?
|
190
|
+
|
191
|
+
# otherwise duplicate and apply skip options
|
192
|
+
pre_checks[pre_checks.index(check)] = check.dup.tap { |c| c.skip! options }
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# rubocop: enable Metrics/AbcSize
|
196
|
+
|
197
|
+
def pre_checks
|
198
|
+
return @pre_checks if instance_variable_defined?(:@pre_checks)
|
199
|
+
|
200
|
+
@pre_checks =
|
201
|
+
if superclass.respond_to?(:pre_checks)
|
202
|
+
superclass.pre_checks.dup
|
203
|
+
else
|
204
|
+
[]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActionPolicy
|
4
|
+
module Policy
|
5
|
+
class FailureReason # :nodoc:
|
6
|
+
attr_reader :policy, :rule
|
7
|
+
|
8
|
+
def initialize(policy_or_class, rule)
|
9
|
+
@policy = policy_or_class.is_a?(Class) ? policy_or_class : policy_or_class.class
|
10
|
+
@rule = rule
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Failures reasons store
|
15
|
+
class FailureReasons
|
16
|
+
include Enumerable
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :@reasons, :size, :empty?, :last, :each
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@reasons = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def add(policy, rule)
|
26
|
+
@reasons << FailureReason.new(policy, rule)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Provides failure reasons tracking functionality.
|
31
|
+
# That allows you to distinguish between the reasons why authorization was rejected.
|
32
|
+
#
|
33
|
+
# It's helpful when you compose policies (i.e. use one policy within another).
|
34
|
+
#
|
35
|
+
# For example:
|
36
|
+
#
|
37
|
+
# class ApplicantPolicy < ApplicationPolicy
|
38
|
+
# def show?
|
39
|
+
# user.has_permission?(:view_applicants) &&
|
40
|
+
# allowed_to?(:show?, object.stage)
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# Now when you receive an exception, you have a reasons object, which contains additional
|
45
|
+
# information about the failure:
|
46
|
+
#
|
47
|
+
# rescue_from ActionPolicy::Unauthorized do |ex|
|
48
|
+
# ex.reasons.messages #=> { stage: [:show] }
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# You can also wrap _local_ rules into `allowed_to?` to populate reasons:
|
52
|
+
#
|
53
|
+
# class ApplicantPolicy < ApplicationPolicy
|
54
|
+
# def show?
|
55
|
+
# allowed_to?(:view_applicants?) &&
|
56
|
+
# allowed_to?(:show?, object.stage)
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def view_applicants?
|
60
|
+
# user.has_permission?(:view_applicants)
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
module Reasons
|
64
|
+
class << self
|
65
|
+
def prepended(base)
|
66
|
+
base.prepend InstanceMethods
|
67
|
+
end
|
68
|
+
|
69
|
+
alias included prepended
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :reasons
|
73
|
+
|
74
|
+
def with_clean_reasons # :nodoc:
|
75
|
+
old_reasons = reasons
|
76
|
+
@reasons = nil
|
77
|
+
res = yield
|
78
|
+
@reasons = old_reasons
|
79
|
+
res
|
80
|
+
end
|
81
|
+
|
82
|
+
module InstanceMethods # :nodoc:
|
83
|
+
def apply(rule)
|
84
|
+
@reasons = FailureReasons.new
|
85
|
+
super
|
86
|
+
end
|
87
|
+
|
88
|
+
# rubocop: disable Metrics/MethodLength
|
89
|
+
def allowed_to?(rule, record = :__undef__, **options)
|
90
|
+
policy = nil
|
91
|
+
|
92
|
+
succeed =
|
93
|
+
if record == :__undef__
|
94
|
+
policy = self
|
95
|
+
with_clean_reasons { apply(rule) }
|
96
|
+
else
|
97
|
+
policy = policy_for(record: record, **options)
|
98
|
+
|
99
|
+
policy.apply(rule)
|
100
|
+
end
|
101
|
+
|
102
|
+
reasons.add(policy, rule) if reasons && !succeed
|
103
|
+
succeed
|
104
|
+
end
|
105
|
+
# rubocop: enable Metrics/MethodLength
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
require "action_policy/behaviour"
|
5
|
+
|
6
|
+
module ActionPolicy
|
7
|
+
# Channel concern.
|
8
|
+
# Add `authorize!` and `allowed_to?` methods.
|
9
|
+
module Channel
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
include ActionPolicy::Behaviour
|
13
|
+
include ActionPolicy::Behaviours::Namespaced
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/concern"
|
4
|
+
require "action_policy/behaviour"
|
5
|
+
|
6
|
+
module ActionPolicy
|
7
|
+
# Raised when `authorize!` hasn't been called for action
|
8
|
+
class UnauthorizedAction < Error
|
9
|
+
def initialize(controller, action)
|
10
|
+
super("Action '#{controller}##{action}' hasn't been authorized")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Controller concern.
|
15
|
+
# Add `authorize!` and `allowed_to?` methods,
|
16
|
+
# provide `verify_authorized` hook.
|
17
|
+
module Controller
|
18
|
+
extend ActiveSupport::Concern
|
19
|
+
|
20
|
+
include ActionPolicy::Behaviour
|
21
|
+
include ActionPolicy::Behaviours::ThreadMemoized
|
22
|
+
include ActionPolicy::Behaviours::Memoized
|
23
|
+
include ActionPolicy::Behaviours::Namespaced
|
24
|
+
|
25
|
+
included do
|
26
|
+
helper_method :allowed_to?
|
27
|
+
|
28
|
+
attr_writer :authorize_count
|
29
|
+
|
30
|
+
protected :authorize_count=, :authorize_count
|
31
|
+
end
|
32
|
+
|
33
|
+
# Authorize action against a policy.
|
34
|
+
#
|
35
|
+
# Policy is inferred from record
|
36
|
+
# (unless explicitly specified through `with` option).
|
37
|
+
#
|
38
|
+
# If action is not provided, it's inferred from `action_name`.
|
39
|
+
#
|
40
|
+
# If record is not provided, tries to infer the resource class
|
41
|
+
# from controller name (i.e. `controller_name.classify.safe_constantize`).
|
42
|
+
#
|
43
|
+
# Raises `ActionPolicy::Unauthorized` if check failed.
|
44
|
+
def authorize!(record = :__undef__, to: nil, **options)
|
45
|
+
record = controller_name.classify.safe_constantize if
|
46
|
+
record == :__undef__
|
47
|
+
|
48
|
+
to ||= :"#{action_name}?"
|
49
|
+
|
50
|
+
super(record, to: to, **options)
|
51
|
+
|
52
|
+
self.authorize_count += 1
|
53
|
+
end
|
54
|
+
|
55
|
+
# Checks that an activity is allowed for the current context (e.g. user).
|
56
|
+
#
|
57
|
+
# If record is not provided, tries to infer the resource class
|
58
|
+
# from controller name (i.e. `controller_name.classify.safe_constantize`).
|
59
|
+
#
|
60
|
+
# Returns true of false.
|
61
|
+
def allowed_to?(rule, record = :__undef__, **options)
|
62
|
+
record = controller_name.classify.safe_constantize if
|
63
|
+
record == :__undef__
|
64
|
+
|
65
|
+
super(rule, record, **options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def verify_authorized
|
69
|
+
raise UnauthorizedAction.new(controller_path, action_name) if
|
70
|
+
authorize_count.zero?
|
71
|
+
end
|
72
|
+
|
73
|
+
def authorize_count
|
74
|
+
@authorize_count ||= 0
|
75
|
+
end
|
76
|
+
|
77
|
+
class_methods do
|
78
|
+
# Adds after_action callback to check that
|
79
|
+
# authorize! method has been called.
|
80
|
+
def verify_authorized(**options)
|
81
|
+
after_action :verify_authorized, **options
|
82
|
+
end
|
83
|
+
|
84
|
+
# Skips verify_authorized after_action callback.
|
85
|
+
def skip_verify_authorized(**options)
|
86
|
+
skip_after_action :verify_authorized, **options\
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|