dry-ability 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitattributes +2 -0
- data/.gitignore +50 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +20 -0
- data/Rakefile +13 -0
- data/dry-ability.gemspec +36 -0
- data/init.rb +1 -0
- data/lib/dry-ability.rb +1 -0
- data/lib/dry/ability.rb +120 -0
- data/lib/dry/ability/container.rb +43 -0
- data/lib/dry/ability/controller.rb +21 -0
- data/lib/dry/ability/controller/dsl.rb +161 -0
- data/lib/dry/ability/controller/mixin.rb +80 -0
- data/lib/dry/ability/controller/resource.rb +218 -0
- data/lib/dry/ability/controller_resource.rb +62 -0
- data/lib/dry/ability/exceptions.rb +36 -0
- data/lib/dry/ability/f.rb +57 -0
- data/lib/dry/ability/inherited_resource.rb +26 -0
- data/lib/dry/ability/key.rb +19 -0
- data/lib/dry/ability/resource_mediator.rb +94 -0
- data/lib/dry/ability/rule.rb +141 -0
- data/lib/dry/ability/rule_interface.rb +27 -0
- data/lib/dry/ability/rules_builder.rb +92 -0
- data/lib/dry/ability/t.rb +45 -0
- data/lib/dry/ability/version.rb +7 -0
- metadata +214 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8d5704a2a2bf2199fda53a0a74556d9f93c1cc82a46ef9d9912d2452f34fce4f
|
4
|
+
data.tar.gz: b9dd8b4243ad05c462fd14a2052c623bac572588941368f3943a91d6228ce548
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 816667eb2d315a2fda11b40df7e1a5bef065f4425cb4a6785e9bcf617c4761670b9b406f3ddfacef5d0ccde498ef9d226c2f6a4a66596ee8dee0b0c1f04a4944
|
7
|
+
data.tar.gz: de539543156d5b2b9878a0057f4e5271100c06d7d74d9fce7e5abe56215d85a6d7a68db281f258838eff31e6bf044d0b125b52de6b766a9d0392f6f6115a3cb6
|
data/.gitattributes
ADDED
data/.gitignore
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
## Specific to RubyMotion:
|
17
|
+
.dat*
|
18
|
+
.repl_history
|
19
|
+
build/
|
20
|
+
*.bridgesupport
|
21
|
+
build-iPhoneOS/
|
22
|
+
build-iPhoneSimulator/
|
23
|
+
|
24
|
+
## Specific to RubyMotion (use of CocoaPods):
|
25
|
+
#
|
26
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
27
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
28
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
29
|
+
#
|
30
|
+
# vendor/Pods/
|
31
|
+
|
32
|
+
## Documentation cache and generated files:
|
33
|
+
/.yardoc/
|
34
|
+
/_yardoc/
|
35
|
+
/doc/
|
36
|
+
/rdoc/
|
37
|
+
|
38
|
+
## Environment normalization:
|
39
|
+
/.bundle/
|
40
|
+
/vendor/bundle
|
41
|
+
/lib/bundler/man/
|
42
|
+
|
43
|
+
# for a library or gem, you might want to ignore these files since the code is
|
44
|
+
# intended to run in multiple environments; otherwise, check them in:
|
45
|
+
# Gemfile.lock
|
46
|
+
# .ruby-version
|
47
|
+
# .ruby-gemset
|
48
|
+
|
49
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
50
|
+
.rvmrc
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.6.5
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2021 Anton
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Dry::Ability
|
2
|
+
|
3
|
+
Dry::Ability is an authorization library, which is trying to replace [cancancan](https://github.com/CanCanCommunity/cancancan) API. The goal is creating less secondary objects by performing authorization. Some codebase, like a controller extension, was copied from cancancan and slightly modified, but public API stays the same. By some reasons (especially, due to integration with InheritedResources) I forked it from version `1.17.0`.
|
4
|
+
|
5
|
+
One significant difference with CanCanCan is that an ability's rules a defined once on class definition and stored into `Dry::Container`.
|
6
|
+
|
7
|
+
Docs, specs & benchmarks is coming soon…
|
8
|
+
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this to your Gemfile:
|
13
|
+
|
14
|
+
gem 'dry-ability'
|
15
|
+
|
16
|
+
and run the `bundle install` command.
|
17
|
+
|
18
|
+
## Getting Started
|
19
|
+
|
20
|
+
Soon…
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
# require 'rspec/core/rake_task'
|
3
|
+
# require 'rubocop/rake_task'
|
4
|
+
|
5
|
+
# desc 'Run Rubocop'
|
6
|
+
# RuboCop::RakeTask.new
|
7
|
+
#
|
8
|
+
# desc 'Run RSpec'
|
9
|
+
# RSpec::Core::RakeTask.new do |t|
|
10
|
+
# t.verbose = false
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# task default: [:rubocop, :spec]
|
data/dry-ability.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require 'dry/ability/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = 'dry-ability'
|
9
|
+
s.version = Dry::Ability::VERSION
|
10
|
+
s.authors = ['Anton Semenov', 'Alessandro Rodi (Renuo AG)', 'Bryan Rite', 'Ryan Bates', 'Richard Wilson']
|
11
|
+
s.email = 'anton.estum@gmail.com'
|
12
|
+
s.homepage = 'https://github.com/estum/dry-ability'
|
13
|
+
s.summary = 'Dried authorization solution for Rails.'
|
14
|
+
s.description = 'Dried authorization solution for Rails. All permissions are stored in a single location.'
|
15
|
+
s.platform = Gem::Platform::RUBY
|
16
|
+
s.license = 'MIT'
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
s.test_files = `git ls-files -- Appraisals {spec,features,gemfiles}/*`.split($INPUT_RECORD_SEPARATOR)
|
20
|
+
s.executables = `git ls-files -- bin/*`.split($INPUT_RECORD_SEPARATOR).map { |f| File.basename(f) }
|
21
|
+
s.require_paths = ['lib']
|
22
|
+
|
23
|
+
s.required_ruby_version = '>= 2.6.0'
|
24
|
+
|
25
|
+
s.add_dependency 'activesupport', '>= 5.2'
|
26
|
+
s.add_dependency 'dry-types', '>= 1.5.0'
|
27
|
+
s.add_dependency 'dry-initializer', '>= 3.0.4'
|
28
|
+
s.add_dependency 'dry-container', '>= 0.7.2'
|
29
|
+
s.add_dependency 'concurrent-ruby', '>= 1.1.8'
|
30
|
+
|
31
|
+
s.add_development_dependency 'bundler', '~> 2.2.15'
|
32
|
+
s.add_development_dependency 'rubocop', '~> 0.46'
|
33
|
+
s.add_development_dependency 'rake', '~> 13.0.3'
|
34
|
+
s.add_development_dependency 'rspec', '~> 3.2.0'
|
35
|
+
s.add_development_dependency 'appraisal', '>= 2.0.0'
|
36
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'dry-ability'
|
data/lib/dry-ability.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "dry/ability"
|
data/lib/dry/ability.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/concern'
|
4
|
+
require 'active_support/core_ext/object/with_options'
|
5
|
+
require 'active_support/core_ext/module/delegation'
|
6
|
+
require 'active_support/core_ext/class/attribute'
|
7
|
+
|
8
|
+
require 'dry/ability/version'
|
9
|
+
require 'dry/ability/exceptions'
|
10
|
+
require "dry/ability/f"
|
11
|
+
require "dry/ability/t"
|
12
|
+
require "dry/ability/key"
|
13
|
+
require 'dry/ability/rules_builder'
|
14
|
+
|
15
|
+
module Dry
|
16
|
+
# Mixin class with DSL to define abilities
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
#
|
20
|
+
# class Ability
|
21
|
+
# include Dry::Ability.define -> do
|
22
|
+
# map_subject! :public => %w(Post Like Comment)
|
23
|
+
#
|
24
|
+
# map_action! :read => %i(index show),
|
25
|
+
# :create => %i(new),
|
26
|
+
# :update => %i(edit),
|
27
|
+
# :crud => %i(index create read show update destroy),
|
28
|
+
# :change => %i(update destroy)
|
29
|
+
#
|
30
|
+
# can :read, :public
|
31
|
+
# can :
|
32
|
+
#
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
module Ability
|
36
|
+
# @private
|
37
|
+
module DSL
|
38
|
+
def define(proc = nil, **options, &block)
|
39
|
+
rules = RulesBuilder.new(**options)
|
40
|
+
rules.instance_exec(&(proc || block))
|
41
|
+
[self, rules.mixin]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
extend ActiveSupport::Concern
|
46
|
+
extend DSL
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
attr_reader :_container
|
50
|
+
alias_method :rules, :_container
|
51
|
+
end
|
52
|
+
|
53
|
+
attr_reader :account
|
54
|
+
|
55
|
+
def initialize(account)
|
56
|
+
@account = account
|
57
|
+
end
|
58
|
+
|
59
|
+
def authorize!(action, subject, message: nil)
|
60
|
+
if can?(action, subject)
|
61
|
+
subject
|
62
|
+
else
|
63
|
+
raise AccessDenied.new(message, action, subject)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def can?(action, subject)
|
68
|
+
rules = resolve_rules(action, subject) do
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
|
72
|
+
rules.reduce(true) do |result, rule|
|
73
|
+
result && rule[@account, subject]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def cannot?(action, subject, *args)
|
78
|
+
!can?(action, subject, *args)
|
79
|
+
end
|
80
|
+
|
81
|
+
def attributes_for(action, subject)
|
82
|
+
rules = resolve_rules(action, subject) do
|
83
|
+
return {}
|
84
|
+
end
|
85
|
+
rules.reduce({}) do |result, rule|
|
86
|
+
result.merge!(rule.attributes_for(@account, subject)); result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def scope_for(action, subject)
|
91
|
+
rules = resolve_rules(action, subject) do
|
92
|
+
return yield if block_given?
|
93
|
+
if subject.respond_to?(:none)
|
94
|
+
return subject.none
|
95
|
+
else
|
96
|
+
raise ArgumentError, "expected subject to be an ActiveRecord::Base class or Relation. given: #{subject}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
if rules.none?(&:accessible?)
|
100
|
+
if block_given?
|
101
|
+
return yield
|
102
|
+
else
|
103
|
+
raise Error, "none of matched rules are provides scope for #{action}, #{subject}, pass block instead"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
rules.map { |rule| rule.scope_for(@account, subject) }.reduce(:merge)
|
107
|
+
end
|
108
|
+
|
109
|
+
def resolve_rules(action, subject)
|
110
|
+
rules.resolve_with_mappings(action, subject) do |e|
|
111
|
+
Rails.logger.warn { e.message }
|
112
|
+
block_given? ? yield : nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
delegate :rules, to: "self.class"
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/container"
|
4
|
+
require "dry/ability/f"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Ability
|
8
|
+
class Container < Dry::Container
|
9
|
+
MAPPING_NSFN = {
|
10
|
+
:subject => F[:string_tpl, "mappings.subject.%s"].to_proc,
|
11
|
+
:action => F[:string_tpl, "mappings.action.%s"].to_proc
|
12
|
+
}
|
13
|
+
RULES_NSFN = F[:string_tpl, "rules.%s.%s"].to_proc
|
14
|
+
|
15
|
+
# @yieldparam exception
|
16
|
+
# Yields block with an instance of +RuleNotDefault+ exception class
|
17
|
+
def resolve_with_mappings(action, subject)
|
18
|
+
candidates = key_candidates(action, subject)
|
19
|
+
result = []
|
20
|
+
candidates.each do |key|
|
21
|
+
next unless key?(key)
|
22
|
+
result << resolve(key)
|
23
|
+
end
|
24
|
+
if result.blank?
|
25
|
+
exception = RuleNotDefined.new(action: action, subject: subject, candidates: candidates)
|
26
|
+
raise exception unless block_given?
|
27
|
+
yield(exception)
|
28
|
+
else
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def key_candidates(action, subject)
|
34
|
+
subject, action = mappings(:subject, subject), mappings(:action, action)
|
35
|
+
subject.product(action).map!(&RULES_NSFN)
|
36
|
+
end
|
37
|
+
|
38
|
+
def mappings(kind, key)
|
39
|
+
F.collect_mappings(key, self, MAPPING_NSFN[kind], &:to_s)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/ability/resource_mediator"
|
4
|
+
require "dry/ability/controller/mixin"
|
5
|
+
require "dry/ability/controller/dsl"
|
6
|
+
require "dry/ability/controller_resource"
|
7
|
+
require "dry/ability/inherited_resource" if defined?(::InheritedResources)
|
8
|
+
|
9
|
+
module Dry
|
10
|
+
module Ability
|
11
|
+
module Controller
|
12
|
+
extend ActiveSupport::Concern
|
13
|
+
|
14
|
+
include Controller::Mixin
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
include Controller::DSL
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "concurrent/map"
|
4
|
+
|
5
|
+
module Dry
|
6
|
+
module Ability
|
7
|
+
module Controller
|
8
|
+
module DSL
|
9
|
+
private def inherited(klass)
|
10
|
+
super(klass)
|
11
|
+
klass.instance_variable_set(:@_resource_mediators, @_resource_mediators.dup)
|
12
|
+
klass.instance_variable_set(:@_cancan_skipper, @_cancan_skipper.dup)
|
13
|
+
klass
|
14
|
+
end
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
def set_resource_mediator_callback(name, **opts)
|
18
|
+
callback_options = opts.extract!(:only, :except, :if, :unless, :prepend)
|
19
|
+
@_resource_mediators ||= Concurrent::Map.new
|
20
|
+
@_resource_mediators.fetch_or_store(name) do
|
21
|
+
ResourceMediator.new(name, controller_path, __callee__, **opts).
|
22
|
+
tap { |m| before_action m, callback_options }
|
23
|
+
end.sequence << __callee__
|
24
|
+
end
|
25
|
+
|
26
|
+
# @!method load_and_authorize_resource(*args, **opts)
|
27
|
+
alias_method :load_and_authorize_resource, :set_resource_mediator_callback
|
28
|
+
|
29
|
+
# @!method load_resource(*args, **opts)
|
30
|
+
# @see #load_and_authorize_resource
|
31
|
+
# @option opts [Array<String,Symbol>] :only Only applies before filter to given actions.
|
32
|
+
# @option opts [Array<String,Symbol>] :except Does not apply before filter to given actions.
|
33
|
+
# @option opts [Boolean] :prepend Prepend callback
|
34
|
+
# @option opts [Symbol] :through Load this resource through another one.
|
35
|
+
# @option opts [Symbol] :through_association
|
36
|
+
# @option opts [Boolean] :shallow (false) allow this resource to be loaded directly when parent is +nil+
|
37
|
+
# @option opts [Boolean] :singleton (false) singleton resource through a +has_one+ association.
|
38
|
+
# @option opts [Boolean] :parent defaults to +true+ if a resource name is given which does not match the controller.
|
39
|
+
# @option opts [String,Class] :class The class to use for the model (string or constant).
|
40
|
+
# @option opts [String,Symbol] :instance_name The name of the instance variable to load the resource into.
|
41
|
+
# @option opts [Symbol] :find_by will use find_by(permalink: params[:id])
|
42
|
+
# @option opts [Symbol] :id_param Find using a param key other than :id. For example:
|
43
|
+
# @option opts [Array<Symbol>] :collection External actions as resource collection
|
44
|
+
# @option opts [Array<Symbol>] :new External actions as new resource
|
45
|
+
alias_method :load_resource, :set_resource_mediator_callback
|
46
|
+
|
47
|
+
# @!method authorize_resource(*args, **opts)
|
48
|
+
# @see #load_and_authorize_resource
|
49
|
+
# @option opts [Array<String,Symbol>] :only Only applies before filter to given actions.
|
50
|
+
# @option opts [Array<String,Symbol>] :except Does not apply before filter to given actions.
|
51
|
+
# @option opts [Boolean] :prepend Prepend callback
|
52
|
+
# @option opts [Boolean] :singleton (false) singleton resource through a +has_one+ association.
|
53
|
+
# @option opts [Boolean] :parent (true)
|
54
|
+
# @option opts [String,Class] :class The class to use for the model (string or constant).
|
55
|
+
# @option opts [String,Symbol] :instance_name The name of the instance variable to load the resource into.
|
56
|
+
# @option opts [Symbol] :through Authorize conditions on this parent resource when instance isn't available.
|
57
|
+
alias_method :authorize_resource, :set_resource_mediator_callback
|
58
|
+
|
59
|
+
undef_method :set_resource_mediator_callback
|
60
|
+
|
61
|
+
# Skip both the loading and authorization behavior of CanCan for this given controller. This is primarily
|
62
|
+
# useful to skip the behavior of a superclass. You can pass :only and :except options to specify which actions
|
63
|
+
# to skip the effects on. It will apply to all actions by default.
|
64
|
+
#
|
65
|
+
# class ProjectsController < SomeOtherController
|
66
|
+
# skip_load_and_authorize_resource :only => :index
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# You can also pass the resource name as the first argument to skip that resource.
|
70
|
+
def skip_load_and_authorize_resource(*args)
|
71
|
+
skip_load_resource(*args)
|
72
|
+
skip_authorize_resource(*args)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Skip the loading behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
76
|
+
# only do authorization on certain actions. You can pass :only and :except options to specify which actions to
|
77
|
+
# skip the effects on. It will apply to all actions by default.
|
78
|
+
#
|
79
|
+
# class ProjectsController < ApplicationController
|
80
|
+
# load_and_authorize_resource
|
81
|
+
# skip_load_resource :only => :index
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# You can also pass the resource name as the first argument to skip that resource.
|
85
|
+
def skip_load_resource(name, **options)
|
86
|
+
cancan_skipper[:load][name] = options
|
87
|
+
end
|
88
|
+
|
89
|
+
# Skip the authorization behavior of CanCan. This is useful when using +load_and_authorize_resource+ but want to
|
90
|
+
# only do loading on certain actions. You can pass :only and :except options to specify which actions to
|
91
|
+
# skip the effects on. It will apply to all actions by default.
|
92
|
+
#
|
93
|
+
# class ProjectsController < ApplicationController
|
94
|
+
# load_and_authorize_resource
|
95
|
+
# skip_authorize_resource :only => :index
|
96
|
+
# end
|
97
|
+
#
|
98
|
+
# You can also pass the resource name as the first argument to skip that resource.
|
99
|
+
def skip_authorize_resource(name, **options)
|
100
|
+
cancan_skipper[:authorize][name] = options
|
101
|
+
end
|
102
|
+
|
103
|
+
# Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
|
104
|
+
# If neither of these authorization methods are called,
|
105
|
+
# a CanCan::AuthorizationNotPerformed exception will be raised.
|
106
|
+
# This is normally added to the ApplicationController to ensure all controller actions do authorization.
|
107
|
+
#
|
108
|
+
# class ApplicationController < ActionController::Base
|
109
|
+
# check_authorization
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# See skip_authorization_check to bypass this check on specific controller actions.
|
113
|
+
#
|
114
|
+
# Options:
|
115
|
+
# [:+only+]
|
116
|
+
# Only applies to given actions.
|
117
|
+
#
|
118
|
+
# [:+except+]
|
119
|
+
# Does not apply to given actions.
|
120
|
+
#
|
121
|
+
# [:+if+]
|
122
|
+
# Supply the name of a controller method to be called.
|
123
|
+
# The authorization check only takes place if this returns true.
|
124
|
+
#
|
125
|
+
# check_authorization :if => :admin_controller?
|
126
|
+
#
|
127
|
+
# [:+unless+]
|
128
|
+
# Supply the name of a controller method to be called.
|
129
|
+
# The authorization check only takes place if this returns false.
|
130
|
+
#
|
131
|
+
# check_authorization :unless => :devise_controller?
|
132
|
+
#
|
133
|
+
def check_authorization(**options)
|
134
|
+
after_action(**options) do |controller|
|
135
|
+
next if controller.instance_variable_defined?(:@_authorized)
|
136
|
+
raise AuthorizationNotPerformed,
|
137
|
+
'This action failed the check_authorization because it does not authorize_resource. '\
|
138
|
+
'Add skip_authorization_check to bypass this check.'
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Call this in the class of a controller to skip the check_authorization behavior on the actions.
|
143
|
+
#
|
144
|
+
# class HomeController < ApplicationController
|
145
|
+
# skip_authorization_check :only => :index
|
146
|
+
# end
|
147
|
+
#
|
148
|
+
# Any arguments are passed to the +before_action+ it triggers.
|
149
|
+
def skip_authorization_check(*args)
|
150
|
+
before_action(*args) do |controller|
|
151
|
+
controller.instance_variable_set(:@_authorized, true)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def cancan_skipper
|
156
|
+
@_cancan_skipper ||= { authorize: {}, load: {} }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|