can4 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8bf6c29d68e2f8a96d458d8baba14d9116f954a47ca29f32dac886942204006a
4
+ data.tar.gz: 41e2092f6921a89df7552db67c56a633f0f5dc893baab3f5bbbc5cbbbd4b6b2a
5
+ SHA512:
6
+ metadata.gz: b3e57d7a3322f23b0d98b5e7963d284532840c6189be4de60c41ae8c17b7e68504cd48c67c8d67271bdbcfee912ceab1f4b8a921e7dab1f7cb1d669896917779
7
+ data.tar.gz: b5dfcd9ea63efe103f94d479009340fdc87059c207c848561f5ad048e1d29c86ef7fa88dfb5e0fea1ba47c9bf07ea43eb123379e731d7cedd40e18edef60f981
@@ -0,0 +1,2 @@
1
+ .yardoc
2
+ /doc
@@ -0,0 +1,2 @@
1
+ --no-private
2
+ --embed-mixin ClassMethods
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Declare your gem's dependencies in can4.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use a debugger
14
+ # gem 'byebug', group: [:development, :test]
15
+ gem 'rubocop'
16
+ gem 'yard'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2019 Liam P. White
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,3 @@
1
+ # Can4
2
+
3
+ An opinionated Ruby ACL module for Rails applications.
@@ -0,0 +1,16 @@
1
+ require_relative 'lib/can4/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'can4'
5
+ s.version = Can4::VERSION
6
+ s.author = 'Liam P. White'
7
+ s.email = 'liamwhite@users.noreply.github.com'
8
+ s.homepage = 'https://github.com/liamwhite/can4'
9
+ s.summary = 'Opinionated ACL framework'
10
+ s.license = 'MIT'
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.require_paths = ['lib']
14
+
15
+ s.add_runtime_dependency 'rails'
16
+ end
@@ -0,0 +1,9 @@
1
+ require 'can4/version'
2
+ require 'can4/rule'
3
+ require 'can4/ability'
4
+ require 'can4/controller_additions'
5
+ require 'can4/exceptions'
6
+
7
+ # A simple, fast authorization framework.
8
+ module Can4
9
+ end
@@ -0,0 +1,111 @@
1
+ module Can4
2
+ # Ability class for resources.
3
+ #
4
+ # To define an ability model for your resource, define an ability class in
5
+ # a location of your choosing, and define the actions available to the
6
+ # resource on construction.
7
+ #
8
+ # @example
9
+ # class Ability < Can4::Ability
10
+ # def initialize(user)
11
+ # # Handle unauthenticated users.
12
+ # user ||= User.new
13
+ #
14
+ # if user.admin?
15
+ # # Allow admins to perform any action.
16
+ # allow_anything!
17
+ # else
18
+ # # Will always return true for can?(:read, @comment).
19
+ # can :read, Comment
20
+ #
21
+ # # Will only return true for can?(:read, @private_message)
22
+ # # if the user is allowed to read the private message.
23
+ # can :read, PrivateMessage do |msg|
24
+ # msg.user_id == user.id
25
+ # end
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ class Ability
31
+ # Checks whether the object can perform an action on a subject.
32
+ #
33
+ # @overload can?(action, subject)
34
+ # @param action [Symbol] The action, represented as a symbol.
35
+ # @param subject [Object] The subject.
36
+ # @overload can?(action, subject, *args)
37
+ # @param action [Symbol] The action, represented as a symbol.
38
+ # @param subject [Object] The subject.
39
+ # @param args [Object] Splat parameters to an installed block.
40
+ # @return [Boolean] True or false.
41
+ def can?(action, subject, *args)
42
+ lookup_rule(subject).authorized?(action, subject, args)
43
+ end
44
+
45
+ # Inverse of #can?.
46
+ #
47
+ # @see #can?
48
+ def cannot?(*args)
49
+ !can?(*args)
50
+ end
51
+
52
+ # Adds an access-granting rule.
53
+ #
54
+ # @param action [Symbol] The action, represented as a symbol.
55
+ # @param subject [Object] The subject.
56
+ # @param block [Proc] An optional Proc to install for matching.
57
+ def can(action, subject, &block)
58
+ rule_for(subject).add_grant(action, block)
59
+ end
60
+
61
+ # Allows the object to perform any action on any subject.
62
+ # This overrides all #cannot rules.
63
+ def allow_anything!
64
+ instance_eval do
65
+ def can?(*)
66
+ true
67
+ end
68
+
69
+ def cannot?(*)
70
+ false
71
+ end
72
+ end
73
+ end
74
+
75
+ # Checks whether this resource has authorization to perform an action on a
76
+ # particular subject. Raises {Can4::AccessDenied} if it doesn't.
77
+ #
78
+ # @param action [Symbol] The intended action.
79
+ # @param subject [Object] The subject of the action.
80
+ # @raise [AccessDenied] if the object does not have permission.
81
+ def authorize!(action, subject, *args)
82
+ raise AccessDenied if cannot?(action, subject, *args)
83
+ end
84
+
85
+ protected
86
+
87
+ # Subjects hash.
88
+ def subjects
89
+ @subjects ||= {}
90
+ end
91
+
92
+ # Find or create a new rule for the specified subject.
93
+ #
94
+ # @param subject [Object] The subject.
95
+ def rule_for(subject)
96
+ subjects[subject] ||= SubjectRule.new
97
+ end
98
+
99
+ # Lookup a rule for a particular subject.
100
+ #
101
+ # @param subject [Object] The subject.
102
+ def lookup_rule(subject)
103
+ case subject
104
+ when Symbol, Module
105
+ subjects[subject] || NullRule
106
+ else
107
+ subjects[subject.class] || NullRule
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Can4
4
+ # Rails controller additions for Can4.
5
+ #
6
+ # In most cases, it is not necessary to define anything here, as it is
7
+ # included for you automatically when +ActionController::Base+ is defined.
8
+ #
9
+ # However, if your controller resource is not defined using a method named
10
+ # +current_user+, or you use different arguments for your +Ability+
11
+ # constructor, you will need to override the +current_ability+ method in
12
+ # your controller.
13
+ #
14
+ # @example
15
+ # class ApplicationController < ActionController::Base
16
+ # # ...
17
+ #
18
+ # private
19
+ #
20
+ # # This example shows a possible redefinition of current_ability
21
+ # # with a different scope and two constructor arguments.
22
+ # def current_ability
23
+ # @current_ability ||= ::Ability.new(current_admin, request.remote_ip)
24
+ # end
25
+ # end
26
+ #
27
+ module ControllerAdditions
28
+ module ClassMethods
29
+ # Add this to a controller to ensure it performs authorization through an
30
+ # {#authorize!} call.
31
+ #
32
+ # If neither of these authorization methods are called, a
33
+ # {Can4::AuthorizationNotPerformed} exception will be raised.
34
+ #
35
+ # This can be placed in your ApplicationController to ensure all
36
+ # controller actions perform authorization.
37
+ def check_authorization(*args)
38
+ after_action(*args) do |controller|
39
+ next if controller.instance_variable_defined?(:@_authorized)
40
+
41
+ raise AuthorizationNotPerformed,
42
+ 'This action failed to check_authorization because it did not ' \
43
+ 'authorize a resource. Add skip_authorization_check to bypass ' \
44
+ 'this check.'
45
+ end
46
+ end
47
+
48
+ # Call this in the class of a controller to skip the check_authorization
49
+ # behavior on the actions. Arguments are the same as +before_action+.
50
+ def skip_authorization_check(*args)
51
+ before_action(*args) do |controller|
52
+ controller.instance_variable_set(:@_authorized, true)
53
+ end
54
+ end
55
+ end
56
+
57
+ # Raises a {Can4::AccessDenied} exception if the current ability cannot
58
+ # perform the given action. This is usually called in a controller action
59
+ # or +before_action+.
60
+ #
61
+ # You can rescue from the exception in the controller to customize how
62
+ # unauthorized access is displayed.
63
+ #
64
+ # @raise [Can4::AccessDenied]
65
+ # The current ability cannot perform the requested action.
66
+ def authorize!(*args)
67
+ @_authorized = true
68
+ current_ability.authorize!(*args)
69
+ end
70
+
71
+ # Creates and returns the current ability and caches it. If you want to
72
+ # override how the +Ability+ is defined, then this is the place. Simply
73
+ # redefine the method in the controller to change its behavior.
74
+ #
75
+ # Note that it is important to memoize the ability object so it is not
76
+ # recreated every time.
77
+ def current_ability
78
+ @current_ability ||= ::Ability.new(current_user)
79
+ end
80
+
81
+ # Use in the controller or view to check the resources's permission for a
82
+ # given action and object. This simply calls #can? on the current ability.
83
+ #
84
+ # @see Ability#can?
85
+ def can?(*args)
86
+ current_ability.can?(*args)
87
+ end
88
+
89
+ # Convenience method which works the same as {#can?}, but returns the
90
+ # opposite value.
91
+ #
92
+ # @see Ability#cannot?
93
+ def cannot?(*args)
94
+ current_ability.cannot?(*args)
95
+ end
96
+
97
+ def self.included(base)
98
+ base.extend ClassMethods
99
+
100
+ return unless base.respond_to?(:helper_method)
101
+
102
+ base.helper_method :can?, :cannot?, :current_ability
103
+ end
104
+ end
105
+ end
106
+
107
+ if defined?(ActionController::Base)
108
+ ActionController::Base.class_eval do
109
+ include Can4::ControllerAdditions
110
+ end
111
+ end
@@ -0,0 +1,10 @@
1
+ module Can4
2
+ # A general exception.
3
+ class Error < StandardError; end
4
+
5
+ # Raised when using +check_authorization+ without calling +authorize!+.
6
+ class AuthorizationNotPerformed < Error; end
7
+
8
+ # Raised when a resource fails a call to +authorize!+.
9
+ class AccessDenied < Error; end
10
+ end
@@ -0,0 +1,43 @@
1
+ module Can4
2
+ # Rule class representing actions performable on a subject.
3
+ # @!visibility private
4
+ class SubjectRule
5
+ def initialize
6
+ @actions = {}
7
+ end
8
+
9
+ # Add a granting ACL for a particular action.
10
+ #
11
+ # @param action [symbol] The action.
12
+ # @param block An optional block for granularity.
13
+ def add_grant(action, block)
14
+ @actions[action] = block || true
15
+ end
16
+
17
+ # Return whether or not an object can perform a particular action on a
18
+ # subject.
19
+ #
20
+ # @param action [Symbol] The action.
21
+ # @param subject [Object] The subject.
22
+ # @param args [Hash] Variable arguments for more granular matching.
23
+ # @return [Boolean] True or false.
24
+ def authorized?(action, subject, args)
25
+ block = @actions[:manage] || @actions[action]
26
+
27
+ return false unless block
28
+ return true if block == true
29
+
30
+ !!block.call(subject, *args)
31
+ end
32
+ end
33
+
34
+ # Fake rule representing nothing matched a subject when looking up its
35
+ # ability.
36
+ #
37
+ # @!visibility private
38
+ class NullRule
39
+ def self.authorized?(*)
40
+ false
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Can4
2
+ VERSION = '1.0.2'.freeze
3
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: can4
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Liam P. White
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-10 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email: liamwhite@users.noreply.github.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - ".gitignore"
34
+ - ".yardopts"
35
+ - Gemfile
36
+ - LICENSE
37
+ - README.md
38
+ - can4.gemspec
39
+ - lib/can4.rb
40
+ - lib/can4/ability.rb
41
+ - lib/can4/controller_additions.rb
42
+ - lib/can4/exceptions.rb
43
+ - lib/can4/rule.rb
44
+ - lib/can4/version.rb
45
+ homepage: https://github.com/liamwhite/can4
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.7.6.2
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Opinionated ACL framework
69
+ test_files: []