corral_acl 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +22 -0
  6. data/README.md +7 -0
  7. data/corral.gemspec +17 -0
  8. data/doc/Ability.html +257 -0
  9. data/doc/Corral/Ability.html +802 -0
  10. data/doc/Corral/AccessDenied.html +146 -0
  11. data/doc/Corral/AuthorizationNotPerformed.html +145 -0
  12. data/doc/Corral/ControllerAdditions/ClassMethods.html +277 -0
  13. data/doc/Corral/ControllerAdditions.html +513 -0
  14. data/doc/Corral/Error.html +141 -0
  15. data/doc/Corral/Generators/AbilityGenerator.html +195 -0
  16. data/doc/Corral/Generators.html +117 -0
  17. data/doc/Corral/NullRule.html +226 -0
  18. data/doc/Corral/SubjectRule.html +406 -0
  19. data/doc/Corral.html +131 -0
  20. data/doc/_index.html +194 -0
  21. data/doc/class_list.html +51 -0
  22. data/doc/css/common.css +1 -0
  23. data/doc/css/full_list.css +58 -0
  24. data/doc/css/style.css +474 -0
  25. data/doc/doc/_index.html +87 -0
  26. data/doc/doc/class_list.html +51 -0
  27. data/doc/doc/css/common.css +1 -0
  28. data/doc/doc/css/full_list.css +58 -0
  29. data/doc/doc/css/style.css +474 -0
  30. data/doc/doc/file_list.html +51 -0
  31. data/doc/doc/frames.html +17 -0
  32. data/doc/doc/index.html +87 -0
  33. data/doc/doc/js/app.js +243 -0
  34. data/doc/doc/js/full_list.js +216 -0
  35. data/doc/doc/js/jquery.js +4 -0
  36. data/doc/doc/method_list.html +51 -0
  37. data/doc/doc/top-level-namespace.html +102 -0
  38. data/doc/file.README.html +82 -0
  39. data/doc/file_list.html +56 -0
  40. data/doc/frames.html +17 -0
  41. data/doc/index.html +82 -0
  42. data/doc/js/app.js +243 -0
  43. data/doc/js/full_list.js +216 -0
  44. data/doc/js/jquery.js +4 -0
  45. data/doc/method_list.html +211 -0
  46. data/doc/top-level-namespace.html +114 -0
  47. data/lib/corral/ability.rb +85 -0
  48. data/lib/corral/controller_additions.rb +76 -0
  49. data/lib/corral/exceptions.rb +10 -0
  50. data/lib/corral/rule.rb +45 -0
  51. data/lib/corral/version.rb +3 -0
  52. data/lib/corral.rb +5 -0
  53. data/lib/generators/corral/ability/USAGE +4 -0
  54. data/lib/generators/corral/ability/ability_generator.rb +11 -0
  55. data/lib/generators/corral/ability/templates/ability.rb +25 -0
  56. data/spec/corral/ability_spec.rb +26 -0
  57. data/spec/spec_helper.rb +97 -0
  58. metadata +101 -0
@@ -0,0 +1,85 @@
1
+ module Corral
2
+ module Ability
3
+ # Check whether the object can perform an action on a subject.
4
+ #
5
+ # @overload can?(action, subject)
6
+ # @param action [Symbol] The action, represented as a symbol.
7
+ # @param subject [Object] The subject.
8
+ # @overload can?(action, subject, args)
9
+ # @param action [Symbol] The action, represented as a symbol.
10
+ # @param subject [Object] The subject.
11
+ # @param args [Hash] Variable arguments for more granular matching.
12
+ # @return [Boolean] True or false.
13
+ def can?(action, subject, *args)
14
+ return true if @allow_anything
15
+ lookup_rule(subject).authorized?(action, subject, args)
16
+ end
17
+
18
+ # Inverse of #can?.
19
+ #
20
+ # @see #can?
21
+ def cannot?(*args)
22
+ not can?(*args)
23
+ end
24
+
25
+ # Adds a granting-access rule.
26
+ #
27
+ # @param action [Symbol] The action, represented as a symbol.
28
+ # @param subject [Object] The subject.
29
+ # @param block [Hash] Variable arguments for more granular matching.
30
+ def can(action, subject, &block)
31
+ rule_for(subject).add_grant(action, block)
32
+ end
33
+
34
+ # Inverse of #can.
35
+ #
36
+ # @see #can
37
+ def cannot(action, subject, &block)
38
+ rule_for(subject).add_deny(action, block)
39
+ end
40
+
41
+ # Allow the object to perform any action on any subject.
42
+ # This overrides any #cannot rules.
43
+ #
44
+ def allow_anything!
45
+ @allow_anything = true
46
+ end
47
+
48
+ # Check whether the object has authorization to perform the
49
+ # action it intends to on the subject. Raise AccessDenied
50
+ # if it doesn't.
51
+ #
52
+ # @param action [Symbol] The intended action.
53
+ # @param subject [Object] The subject of the action.
54
+ # @raise [AccessDenied] if the object does not have permission.
55
+ def authorize!(action, subject, *args)
56
+ raise AccessDenied if cannot?(action, subject, *args)
57
+ end
58
+
59
+ protected
60
+
61
+ # Subjects hash.
62
+ def subjects
63
+ @subjects ||= {}
64
+ end
65
+
66
+ # Find or create a new rule for the specified subject.
67
+ #
68
+ # @param subject [Object] The subject.
69
+ def rule_for(subject)
70
+ subjects[subject] ||= SubjectRule.new
71
+ end
72
+
73
+ # Lookup a rule for a particular subject.
74
+ #
75
+ # @param subject [Object] The subject.
76
+ def lookup_rule(subject)
77
+ case subject
78
+ when Symbol, Class
79
+ r = subjects[subject] || subjects[:all] || NullRule
80
+ else
81
+ subjects[subject.class] || NullRule
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,76 @@
1
+ module Corral
2
+ module ControllerAdditions
3
+ module ClassMethods
4
+ # Add this to a controller to ensure it performs authorization through +authorized+! or +authorize_resource+ call.
5
+ # If neither of these authorization methods are called, a Corral::AuthorizationNotPerformed exception will be raised.
6
+ # This can be placed in ApplicationController to ensure all controller actions do authorization.
7
+ def check_authorization(options = {})
8
+ self.after_filter(options.slice(:only, :except)) do |controller|
9
+ next if controller.instance_variable_defined?(:@_authorized)
10
+ next if options[:if] && !controller.send(options[:if])
11
+ next if options[:unless] && controller.send(options[:unless])
12
+ raise AuthorizationNotPerformed, "This action failed the check_authorization because it did not authorize a resource. Add skip_authorization_check to bypass this check."
13
+ end
14
+ end
15
+
16
+ # Call this in the class of a controller to skip the check_authorization behavior on the actions.
17
+ # Any arguments are passed to the +before_filter+ called.
18
+ def skip_authorization_check(*args)
19
+ self.before_filter(*args) do |controller|
20
+ controller.instance_variable_set(:@_authorized, true)
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.included(base)
26
+ base.extend ClassMethods
27
+ base.helper_method :can?, :cannot?, :current_ability if base.respond_to? :helper_method
28
+ end
29
+
30
+ # Raises a Corral::AccessDenied exception if the current_ability cannot
31
+ # perform the given action. This is usually called in a controller action or
32
+ # before filter to perform the authorization.
33
+ #
34
+ # A :message option can be passed to specify a different message.
35
+ #
36
+ # You can rescue from the exception in the controller to customize how unauthorized
37
+ # access is displayed to the user.
38
+ #
39
+ # See the load_and_authorize_resource method to automatically add the authorize! behavior
40
+ # to the default RESTful actions.
41
+ def authorize!(*args)
42
+ @_authorized = true
43
+ current_ability.authorize!(*args)
44
+ end
45
+
46
+ # Creates and returns the current user's ability and caches it. If you
47
+ # want to override how the Ability is defined then this is the place.
48
+ # Just define the method in the controller to change behavior.
49
+ #
50
+ # Notice it is important to memoize the ability object so it is not
51
+ # recreated every time.
52
+ def current_ability
53
+ @current_ability ||= ::Ability.new(current_user)
54
+ end
55
+
56
+ # Use in the controller or view to check the user's permission for a given action
57
+ # and object.
58
+ #
59
+ # This simply calls "can?" on the current_ability. See Ability#can?.
60
+ def can?(*args)
61
+ current_ability.can?(*args)
62
+ end
63
+
64
+ # Convenience method which works the same as "can?" but returns the opposite value.
65
+ #
66
+ def cannot?(*args)
67
+ current_ability.cannot?(*args)
68
+ end
69
+ end
70
+ end
71
+
72
+ if defined? ActionController::Base
73
+ ActionController::Base.class_eval do
74
+ include Corral::ControllerAdditions
75
+ end
76
+ end
@@ -0,0 +1,10 @@
1
+ module Corral
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
+ # This error is raised when a user isn't allowed to access a given controller action.
9
+ class AccessDenied < Error; end
10
+ end
@@ -0,0 +1,45 @@
1
+ module Corral
2
+ # Rule class representing actions doable 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
+ # Add a denying ACL for a particular action.
18
+ #
19
+ # @param action [symbol] The action.
20
+ # @param block An optional block for granularity.
21
+ def add_deny(action, block)
22
+ @actions[action] = (block || false)
23
+ end
24
+
25
+ # Return whether or not an object can perform a particular action on a subject
26
+ # @param action [Symbol] The action.
27
+ # @param subject [Object] The subject.
28
+ # @param args [Hash] Variable arguments for more granular matching.
29
+ # @return [Boolean] True or false.
30
+ def authorized?(action, subject, args)
31
+ block = @actions[:manage] || @actions[action]
32
+ return false unless block
33
+ return true if block == true
34
+ return !!block.call(*[subject, *args])
35
+ end
36
+ end
37
+
38
+ # Fake rule representing nothing matched a subject when looking up its ability.
39
+ # @!visibility private
40
+ class NullRule # :nodoc:
41
+ def self.authorized?(*)
42
+ false
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module Corral
2
+ VERSION = "0.9.0"
3
+ end
data/lib/corral.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'corral/version'
2
+ require 'corral/rule'
3
+ require 'corral/ability'
4
+ require 'corral/controller_additions'
5
+ require 'corral/exceptions'
@@ -0,0 +1,4 @@
1
+ Description:
2
+ The corral:ability generator creates an Ability class in the models
3
+ directory. You can move this file anywhere you want as long as it
4
+ is in the load path.
@@ -0,0 +1,11 @@
1
+ module Corral
2
+ module Generators
3
+ class AbilityGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def generate_ability
7
+ copy_file "ability.rb", "app/models/ability.rb"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ class Ability
2
+ include Corral::Ability
3
+
4
+ def initialize(user)
5
+ # Define abilities for the passed in user here. For example:
6
+ #
7
+ # user ||= User.new # guest user (not logged in)
8
+ # if user.admin?
9
+ # allow_anything!
10
+ # else
11
+ # can :read, Post
12
+ # end
13
+ #
14
+ # The first argument to `can` is the action you are giving the user
15
+ # permission to do.
16
+ # If you pass :manage it will apply to every action. Other common actions
17
+ # here are :read, :create, :update and :destroy.
18
+ #
19
+ # The second argument is the resource the user can perform the action on.
20
+ # Pass a Ruby class of the resource.
21
+ #
22
+ # can :update, Article
23
+ #
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ describe Corral::Ability do
4
+ before :each do
5
+ (@ability = double).extend Corral::Ability
6
+ end
7
+
8
+ it "is able to do anything" do
9
+ @ability.allow_anything!
10
+ expect(@ability.can?(:manage, String)).to be true
11
+ expect(@ability.cannot?(:manage, String)).to be false
12
+ end
13
+
14
+ it "is able to perform a specific action on everything" do
15
+ @ability.can(:read, :all)
16
+ expect(@ability.can?(:read, String)).to be true
17
+ expect(@ability.can?(:write, String)).to be false
18
+ end
19
+
20
+ it "cannot do something it has been told it cannot do" do
21
+ @ability.can(:read, :all)
22
+ expect(@ability.can?(:read, :this)).to be true
23
+ @ability.cannot(:read, :this)
24
+ expect(@ability.can?(:read, :this)).to be false
25
+ end
26
+ end
@@ -0,0 +1,97 @@
1
+ require 'corral'
2
+ # This file was generated by the `rspec --init` command. Conventionally, all
3
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
4
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
5
+ # this file to always be loaded, without a need to explicitly require it in any
6
+ # files.
7
+ #
8
+ # Given that it is always loaded, you are encouraged to keep this file as
9
+ # light-weight as possible. Requiring heavyweight dependencies from this file
10
+ # will add to the boot time of your test suite on EVERY test run, even for an
11
+ # individual file that may not need all of that loaded. Instead, consider making
12
+ # a separate helper file that requires the additional dependencies and performs
13
+ # the additional setup, and require it from the spec files that actually need
14
+ # it.
15
+ #
16
+ # The `.rspec` file also contains a few flags that are not defaults but that
17
+ # users commonly want.
18
+ #
19
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
20
+ RSpec.configure do |config|
21
+ # rspec-expectations config goes here. You can use an alternate
22
+ # assertion/expectation library such as wrong or the stdlib/minitest
23
+ # assertions if you prefer.
24
+ config.expect_with :rspec do |expectations|
25
+ # This option will default to `true` in RSpec 4. It makes the `description`
26
+ # and `failure_message` of custom matchers include text for helper methods
27
+ # defined using `chain`, e.g.:
28
+ # be_bigger_than(2).and_smaller_than(4).description
29
+ # # => "be bigger than 2 and smaller than 4"
30
+ # ...rather than:
31
+ # # => "be bigger than 2"
32
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
33
+ end
34
+
35
+ # rspec-mocks config goes here. You can use an alternate test double
36
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
37
+ config.mock_with :rspec do |mocks|
38
+ # Prevents you from mocking or stubbing a method that does not exist on
39
+ # a real object. This is generally recommended, and will default to
40
+ # `true` in RSpec 4.
41
+ mocks.verify_partial_doubles = true
42
+ end
43
+
44
+ # The settings below are suggested to provide a good initial experience
45
+ # with RSpec, but feel free to customize to your heart's content.
46
+ =begin
47
+ # These two settings work together to allow you to limit a spec run
48
+ # to individual examples or groups you care about by tagging them with
49
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
50
+ # get run.
51
+ config.filter_run :focus
52
+ config.run_all_when_everything_filtered = true
53
+
54
+ # Allows RSpec to persist some state between runs in order to support
55
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
56
+ # you configure your source control system to ignore this file.
57
+ config.example_status_persistence_file_path = "spec/examples.txt"
58
+
59
+ # Limits the available syntax to the non-monkey patched syntax that is
60
+ # recommended. For more details, see:
61
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
62
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
63
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
64
+ config.disable_monkey_patching!
65
+
66
+ # This setting enables warnings. It's recommended, but in some cases may
67
+ # be too noisy due to issues in dependencies.
68
+ config.warnings = true
69
+
70
+ # Many RSpec users commonly either run the entire suite or an individual
71
+ # file, and it's useful to allow more verbose output when running an
72
+ # individual spec file.
73
+ if config.files_to_run.one?
74
+ # Use the documentation formatter for detailed output,
75
+ # unless a formatter has already been configured
76
+ # (e.g. via a command-line flag).
77
+ config.default_formatter = 'doc'
78
+ end
79
+
80
+ # Print the 10 slowest examples and example groups at the
81
+ # end of the spec run, to help surface which specs are running
82
+ # particularly slow.
83
+ config.profile_examples = 10
84
+
85
+ # Run specs in random order to surface order dependencies. If you find an
86
+ # order dependency and want to debug it, you can fix the order by providing
87
+ # the seed, which is printed after each run.
88
+ # --seed 1234
89
+ config.order = :random
90
+
91
+ # Seed global randomization in this process using the `--seed` CLI option.
92
+ # Setting this allows you to use `--seed` to deterministically reproduce
93
+ # test failures related to randomization by passing the same `--seed` value
94
+ # as the one that triggered the failure.
95
+ Kernel.srand config.seed
96
+ =end
97
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: corral_acl
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Arcaire
8
+ - Liam White
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2016-07-06 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Yet another authorization solution.
15
+ email: "/dev/null"
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".gitignore"
21
+ - ".rspec"
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - corral.gemspec
26
+ - doc/Ability.html
27
+ - doc/Corral.html
28
+ - doc/Corral/Ability.html
29
+ - doc/Corral/AccessDenied.html
30
+ - doc/Corral/AuthorizationNotPerformed.html
31
+ - doc/Corral/ControllerAdditions.html
32
+ - doc/Corral/ControllerAdditions/ClassMethods.html
33
+ - doc/Corral/Error.html
34
+ - doc/Corral/Generators.html
35
+ - doc/Corral/Generators/AbilityGenerator.html
36
+ - doc/Corral/NullRule.html
37
+ - doc/Corral/SubjectRule.html
38
+ - doc/_index.html
39
+ - doc/class_list.html
40
+ - doc/css/common.css
41
+ - doc/css/full_list.css
42
+ - doc/css/style.css
43
+ - doc/doc/_index.html
44
+ - doc/doc/class_list.html
45
+ - doc/doc/css/common.css
46
+ - doc/doc/css/full_list.css
47
+ - doc/doc/css/style.css
48
+ - doc/doc/file_list.html
49
+ - doc/doc/frames.html
50
+ - doc/doc/index.html
51
+ - doc/doc/js/app.js
52
+ - doc/doc/js/full_list.js
53
+ - doc/doc/js/jquery.js
54
+ - doc/doc/method_list.html
55
+ - doc/doc/top-level-namespace.html
56
+ - doc/file.README.html
57
+ - doc/file_list.html
58
+ - doc/frames.html
59
+ - doc/index.html
60
+ - doc/js/app.js
61
+ - doc/js/full_list.js
62
+ - doc/js/jquery.js
63
+ - doc/method_list.html
64
+ - doc/top-level-namespace.html
65
+ - lib/corral.rb
66
+ - lib/corral/ability.rb
67
+ - lib/corral/controller_additions.rb
68
+ - lib/corral/exceptions.rb
69
+ - lib/corral/rule.rb
70
+ - lib/corral/version.rb
71
+ - lib/generators/corral/ability/USAGE
72
+ - lib/generators/corral/ability/ability_generator.rb
73
+ - lib/generators/corral/ability/templates/ability.rb
74
+ - spec/corral/ability_spec.rb
75
+ - spec/spec_helper.rb
76
+ homepage:
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.5.1
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Extremely simple authorization solution.
100
+ test_files: []
101
+ has_rdoc: