access_policy 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d6fec0043cb9fd0e0e318c74c3a123d5928373e
4
+ data.tar.gz: 416b4171c59eab9dd40e2235e9059cd9a9b8c914
5
+ SHA512:
6
+ metadata.gz: aa415b1b7d75b12278d6c0edc38d79d2558c912a44d3486b2f3a719fa01bee235eeb80716a84a78e4aa00894614a9ac62cfcf3a72802cbcf27790a891be12780
7
+ data.tar.gz: 51a51cc18979bed2caf2795cdab657a3137bfb4eae275d2385b2af84c9f0a991332d175482580c49819560f976b5be03d8c04613e00b5a09854e32cb1b597727
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .version.conf
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ coverage
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format Fuubar
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
4
+ notifications:
5
+ recipients:
6
+ - shad0wrunner@gmx.de
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in access_policy.gemspec
4
+ gemspec
5
+
6
+ gem 'simplecov', require: false, group: :test
7
+ gem 'coveralls', require: false
data/Guardfile ADDED
@@ -0,0 +1,25 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec, all_after_pass: true ,
5
+ all_on_start: true do
6
+
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+
9
+ watch('lib/yaoc.rb') { "spec" }
10
+
11
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/lib/#{m[1]}_spec.rb" }
12
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/integration/lib/#{m[1]}_spec.rb" }
13
+
14
+ watch('spec/spec_helper.rb') { "spec" }
15
+
16
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
17
+
18
+ end
19
+
20
+
21
+ guard :bundler do
22
+ watch('Gemfile')
23
+ # Uncomment next line if your Gemfile contains the `gemspec' command.
24
+ watch(/^.+\.gemspec/)
25
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dieter Späth
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.
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # AccessPolicy
2
+
3
+ Object oriented authorization for ruby. It provides helper to
4
+ protect method call's via Policy-Classes.
5
+
6
+ Inspired by https://github.com/elabs/pundit, but without Rails
7
+ and with a threat local storage for the current user or role.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'access_policy'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install access_policy
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+
27
+ class ToGuard
28
+ def method_to_guard
29
+
30
+ end
31
+ end
32
+
33
+ ToGuardPolicy = Struct.new(:current_user_or_role, :object_of_kind_to_guard) do
34
+
35
+ def method_to_guard?
36
+ current_user_or_role.is_allowed?
37
+ end
38
+
39
+ end
40
+
41
+ object_to_guard = ToGuard.new
42
+
43
+ policy_checker = AccessPolicy::PolicyCheck.new
44
+
45
+ policy_checker.current_user_or_role_for_policy = current_user
46
+
47
+ policy_checker.with_user_or_role(current_user) do
48
+ begin
49
+ policy_checker.authorize(object_to_guard, 'method_to_guard')
50
+ object_to_guard.method_to_guard
51
+ rescue AccessPolicy::PolicyEnforcer::NotAuthorizedError
52
+ ...
53
+ rescue AccessPolicy::PolicyEnforcer::NotDefinedError
54
+ ...
55
+ end
56
+ end
57
+
58
+
59
+ ```
60
+
61
+ Or
62
+
63
+ ```ruby
64
+
65
+ class ToGuard
66
+ include AccessPolicy
67
+
68
+ policy_guarded_method 'method_to_guard' do
69
+ # do some stuff
70
+ end
71
+
72
+ end
73
+
74
+ ToGuardPolicy = Struct.new(:current_user_or_role, :object_of_kind_to_guard) do
75
+
76
+ def method_to_guard?
77
+ current_user_or_role.is_allowed?
78
+ end
79
+
80
+ end
81
+
82
+ object_to_guard = ToGuard.new
83
+
84
+ object_to_guard.with_user_or_role(current_user) do
85
+ begin
86
+ object_to_guard.method_to_guard
87
+ rescue PolicyEnforcer::NotAuthorizedError
88
+ ...
89
+ rescue PolicyEnforcer::NotDefinedError
90
+ ...
91
+ end
92
+ end
93
+
94
+ object_to_guard.with_user_or_role(current_user) do
95
+ begin
96
+ object_to_guard.method_to_guard
97
+
98
+ object_to_guard.with_user_or_role(current_user.as_root) do
99
+ object_to_guard.method_to_guard_for_root
100
+ end
101
+
102
+ rescue PolicyEnforcer::NotAuthorizedError
103
+ ...
104
+ rescue PolicyEnforcer::NotDefinedError
105
+ ...
106
+ end
107
+ end
108
+
109
+
110
+ ```
111
+
112
+ ## Contributing
113
+
114
+ 1. Fork it ( http://github.com/slowjack2k/access_policy/fork )
115
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
116
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
117
+ 4. Push to the branch (`git push origin my-new-feature`)
118
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+
7
+ task :default => :spec
8
+ task :test => :spec
9
+
10
+ desc "Run RSpec with code coverage"
11
+ task :coverage do
12
+ ENV['SIMPLE_COVERAGE'] = "true"
13
+ Rake::Task["spec"].execute
14
+ end
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'access_policy/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "access_policy"
8
+ spec.version = AccessPolicy::VERSION
9
+ spec.authors = ["Dieter Späth"]
10
+ spec.email = ["shad0wrunner@gmx.de"]
11
+ spec.summary = %q{Object oriented authorization for ruby.}
12
+ spec.description = %q{Object oriented authorization for ruby.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency 'scoped_storage'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.5"
24
+ spec.add_development_dependency "rake"
25
+
26
+
27
+ spec.add_development_dependency "rspec", "2.99.0.beta1"
28
+ # show nicely how many specs have to be run
29
+ spec.add_development_dependency "fuubar"
30
+ # extended console
31
+ spec.add_development_dependency "pry"
32
+ spec.add_development_dependency 'pry-remote'
33
+
34
+ # automatic execute tasks on file changes
35
+ spec.add_development_dependency 'guard'
36
+ spec.add_development_dependency 'guard-rspec'
37
+ spec.add_development_dependency 'guard-bundler'
38
+ spec.add_development_dependency 'rb-fsevent'
39
+
40
+ # https://github.com/pry/pry-stack_explorer
41
+ spec.add_development_dependency 'pry-stack_explorer'
42
+ # https://github.com/nixme/pry-debugger
43
+ spec.add_development_dependency 'pry-debugger'
44
+ end
@@ -0,0 +1,50 @@
1
+ require "access_policy/version"
2
+ require 'scoped_storage'
3
+
4
+ require 'access_policy/policy_check'
5
+ require 'access_policy/policy_enforcer'
6
+
7
+ module AccessPolicy
8
+
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ def _default_error_policy
14
+ ->(*){raise}
15
+ end
16
+
17
+ def _scope_storage
18
+ ScopedStorage::ThreadLocalStorage
19
+ end
20
+
21
+ def _guard
22
+ @_guard ||= PolicyCheck.new(default_error_policy: _default_error_policy,
23
+ scope_storage: _scope_storage
24
+ )
25
+ end
26
+
27
+ def _authorize(query)
28
+ _guard.authorize self, query.to_sym
29
+ end
30
+
31
+ def with_user_or_role(user_or_role, error_policy = _default_error_policy ,&block)
32
+ _guard.with_user_or_role(user_or_role, error_policy, &block)
33
+ end
34
+
35
+ module ClassMethods
36
+
37
+ def policy_guarded_method(action_name, &block)
38
+ unsafe_action_name = :"#{action_name}_unsafe"
39
+
40
+ define_method action_name do |*args|
41
+ _authorize "#{action_name}?"
42
+ self.send(unsafe_action_name, *args)
43
+ end
44
+
45
+ define_method unsafe_action_name, block
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,75 @@
1
+ module AccessPolicy
2
+
3
+ class PolicyCheck
4
+
5
+ attr_accessor :default_error_policy, :scope_storage
6
+
7
+ def initialize(default_error_policy: ->(*) { raise },
8
+ scope_storage: ScopedStorage::ThreadLocalStorage)
9
+
10
+ self.default_error_policy = default_error_policy
11
+ self.scope_storage = scope_storage
12
+ end
13
+
14
+
15
+ def authorize(object_to_guard, action_to_guard, error_policy: default_error_policy)
16
+ PolicyEnforcer.new(current_user_or_role_for_policy, object_to_guard, action_to_guard).authorize(error_policy) do
17
+ self.policy_authorized=true
18
+ end
19
+ end
20
+
21
+ def policy_for(object_or_class, error_policy = default_error_policy)
22
+ PolicyEnforcer.new(current_user_or_role_for_policy, object_or_class).policy(error_policy)
23
+ end
24
+
25
+ def with_user_or_role(new_current_user_or_role_for_policy, error_policy = default_error_policy)
26
+ self.policy_authorized = false
27
+
28
+ switched_user_or_role(new_current_user_or_role_for_policy) do
29
+ begin
30
+ yield if block_given?
31
+ raise(PolicyEnforcer::NotAuthorizedError, "#{new_current_user_or_role_for_policy}") unless policy_authorized?
32
+ rescue => e
33
+ error_policy.call(e)
34
+ end
35
+ end
36
+ end
37
+
38
+ def current_user_or_role_for_policy=(new_user)
39
+ scope['current_user_or_role_for_policy'] = new_user
40
+ end
41
+
42
+ def current_user_or_role_for_policy
43
+ scope['current_user_or_role_for_policy']
44
+ end
45
+
46
+ def policy_authorized=(new_value)
47
+ scope['policy_authorized'] = new_value
48
+ end
49
+
50
+ def policy_authorized?
51
+ !!policy_authorized
52
+ end
53
+
54
+ protected
55
+
56
+ def policy_authorized
57
+ scope['policy_authorized']
58
+ end
59
+
60
+ def scope
61
+ @scope ||= ScopedStorage::Scope.new('policy_infos', scope_storage)
62
+ end
63
+
64
+ def switched_user_or_role(new_current_user_or_role_for_policy)
65
+ old_current_user_or_role = self.current_user_or_role_for_policy
66
+ self.current_user_or_role_for_policy = new_current_user_or_role_for_policy
67
+
68
+ yield if block_given?
69
+
70
+ ensure
71
+ self.current_user_or_role_for_policy = old_current_user_or_role
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,65 @@
1
+ module AccessPolicy
2
+ class PolicyEnforcer
3
+ class NotDefinedError < StandardError;
4
+ end
5
+ class NotAuthorizedError < StandardError;
6
+ end
7
+
8
+ attr_accessor :current_user_or_role, :object_or_class, :query, :default_error_policy
9
+
10
+ def initialize(current_user_or_role, object_or_class, query=nil, default_error_policy=->(*) { raise })
11
+ raise NotDefinedError, 'unable to find policy class for anonymous classes' if class_to_guard(object_or_class).name.nil? || class_to_guard(object_or_class).name.length < 1
12
+
13
+ self.current_user_or_role = current_user_or_role
14
+ self.object_or_class = object_or_class
15
+ self.query = query
16
+ self.default_error_policy = default_error_policy
17
+
18
+ end
19
+
20
+ def authorize(error_policy=default_error_policy)
21
+ raise(PolicyEnforcer::NotAuthorizedError, "not allowed to #{query} this #{object_or_class}" ) unless _guard_action()
22
+ yield true if block_given?
23
+ true
24
+ rescue
25
+ error_policy.call(object_or_class)
26
+ end
27
+
28
+ def policy(error_policy=default_error_policy)
29
+ specific_policy_for_class.new(current_user_or_role, object_or_class)
30
+ rescue
31
+ error_policy.call(object_or_class)
32
+ end
33
+
34
+ def query=(new_query)
35
+ new_query = new_query.to_s
36
+ @query = (new_query.end_with?('?') ? new_query : "#{new_query}?").to_sym
37
+ end
38
+
39
+ protected
40
+
41
+ def class_to_guard(obj_or_class=object_or_class)
42
+ obj_or_class.is_a?(Class) ? obj_or_class : obj_or_class.class
43
+ end
44
+
45
+ def default_policy_name
46
+ "#{class_to_guard.name}Policy"
47
+ end
48
+
49
+ def _guard_action
50
+ policy(->(*) { raise }).public_send(query)
51
+ rescue NoMethodError
52
+ raise NotDefinedError, "unable to find policy method #{query} for #{policy}"
53
+ end
54
+
55
+ def specific_policy_for_class
56
+
57
+ policy_class = class_to_guard.policy_class if class_to_guard.respond_to? :policy_class
58
+ policy_class || Object.const_get(default_policy_name, false)
59
+ rescue
60
+ raise NotDefinedError, "unable to find policy class #{default_policy_name}"
61
+ end
62
+
63
+
64
+ end
65
+ end
@@ -0,0 +1,81 @@
1
+ module AccessPolicy
2
+ module RspecMatchers
3
+ extend ::RSpec::Matchers::DSL
4
+
5
+ def self.included(base)
6
+ base.metadata[:type] = :policy
7
+ end
8
+
9
+ module Common
10
+ def self.call(base)
11
+ base.instance_eval do
12
+ chain :to do |user|
13
+ @user = user
14
+ end
15
+
16
+ chain :on do |object_to_guard|
17
+ @object_to_guard = object_to_guard
18
+ end
19
+
20
+ define_method :permission_granted? do |policy_class, permission |
21
+ policy = policy_class.new(@user, @object_to_guard)
22
+ policy.public_send("#{permission}?")
23
+ end
24
+
25
+ define_method :permission_denied? do |*args|
26
+ ! permission_granted?(*args)
27
+ end
28
+
29
+ define_method :object_as_text do
30
+ @object_to_guard.nil? ? '' : " on #{@object_to_guard.inspect}"
31
+ end
32
+
33
+ define_method :user_as_text do
34
+ @user.nil? ? '' : " for #{@user.inspect}"
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ matcher :permit do |permission|
43
+ match do |policy_class|
44
+ permission_granted?(policy_class, permission)
45
+ end
46
+
47
+ failure_message_for_should do |policy_class|
48
+ "#{policy_class} does not permit #{permission} #{object_as_text}#{user_as_text}."
49
+ end
50
+
51
+ failure_message_for_should_not do |policy_class|
52
+ "#{policy_class} does not forbid #{permission}#{object_as_text}#{user_as_text}."
53
+ end
54
+
55
+ Common.call(self)
56
+
57
+ end
58
+
59
+ matcher :forbid do |permission|
60
+ match do |policy_class|
61
+ permission_denied?(policy_class, permission)
62
+ end
63
+
64
+ failure_message_for_should do |policy_class|
65
+ "#{policy_class} does not forbid #{permission} #{object_as_text}#{user_as_text}."
66
+ end
67
+
68
+ failure_message_for_should_not do |policy_class|
69
+ "#{policy_class} does not permit #{permission}#{object_as_text}#{user_as_text}."
70
+ end
71
+
72
+ Common.call(self)
73
+ end
74
+ end
75
+ end
76
+
77
+ RSpec.configure do |config|
78
+ config.include AccessPolicy::RspecMatchers, type: :policy, example_group: ->(example_group, metadata){
79
+ metadata[:type].nil? && %r{spec/policies} =~ example_group[:file_path]
80
+ }
81
+ end
@@ -0,0 +1,3 @@
1
+ module AccessPolicy
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ require 'access_policy/rspec_matchers'
3
+
4
+ module MatcherExampleSpec
5
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
6
+ def read?
7
+ true
8
+ end
9
+
10
+ def write?
11
+ current_user && current_user.allowed?
12
+ end
13
+
14
+ def destroy?
15
+ write? && object_to_guard && !object_to_guard.locked?
16
+ end
17
+ end
18
+ end
19
+
20
+ describe "PolicyMatchers", type: :policy do
21
+ subject(:policy){
22
+ MatcherExampleSpec::DummyPolicy
23
+ }
24
+
25
+
26
+ context 'for a visitor' do
27
+ let(:visitor){nil}
28
+
29
+ it 'permits read' do
30
+ expect(policy).to permit(:read).to(visitor)
31
+ end
32
+
33
+ it 'forbids write' do
34
+ expect(policy).not_to permit(:write).to(visitor)
35
+ end
36
+ end
37
+
38
+ context 'for a admin' do
39
+ let(:admin){double('admin', allowed?: true)}
40
+ let(:locked_object_to_guard){double('object to guard', locked?: true)}
41
+ let(:unlocked_object_to_guard){double('object to guard', locked?: false)}
42
+
43
+ it 'permits read' do
44
+ expect(policy).to permit(:read).to(admin)
45
+ end
46
+
47
+ it 'permits write' do
48
+ expect(policy).to permit(:write).to(admin)
49
+ end
50
+
51
+ it 'permits destroy for unlocked objects' do
52
+ expect(policy).to permit(:destroy).on(unlocked_object_to_guard).to(admin)
53
+ end
54
+
55
+ it 'forbids destroy for locked objects' do
56
+ expect(policy).to forbid(:destroy).on(locked_object_to_guard).to(admin)
57
+ end
58
+
59
+ end
60
+
61
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module AccessPolicySpec
4
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
5
+ def call?
6
+ current_user && current_user.allowed?
7
+ end
8
+ end
9
+
10
+
11
+ class Dummy
12
+ include AccessPolicy
13
+
14
+ policy_guarded_method 'call' do
15
+ :return_value
16
+ end
17
+
18
+ end
19
+ end
20
+
21
+ describe AccessPolicy do
22
+ subject{
23
+ AccessPolicySpec::Dummy.new
24
+ }
25
+
26
+ let(:falsy_user){
27
+ double("user", allowed?: false)
28
+ }
29
+
30
+ let(:truethy_user){
31
+ double("user", allowed?: true)
32
+ }
33
+
34
+
35
+
36
+ describe '.policy_guarded_method' do
37
+ it 'creates a guarded method' do
38
+ expect(subject).to respond_to :call
39
+ end
40
+
41
+ it 'protects created methods' do
42
+ expect {
43
+ subject.call
44
+ }.to raise_error AccessPolicy::PolicyEnforcer::NotAuthorizedError
45
+ end
46
+
47
+ it 'grands access to guarded methods when the user has the right' do
48
+ expect {
49
+ subject.with_user_or_role(truethy_user) do
50
+ subject.call
51
+ end
52
+ }.not_to raise_error
53
+ end
54
+
55
+ it 'creates a not guarded method' do
56
+ expect(subject).to respond_to :call_unsafe
57
+ end
58
+
59
+ it 'does not protect unsafe methods' do
60
+ expect {
61
+ subject.call_unsafe
62
+ }.not_to raise_error
63
+ end
64
+ end
65
+
66
+ describe '.with_user_or_role' do
67
+ it 'delegates to the policy check object' do
68
+ error_policy = ->(exception){}
69
+ current_user = double('user')
70
+ expect(subject._guard).to receive(:with_user_or_role).with(current_user, error_policy)
71
+
72
+ subject.with_user_or_role(current_user, error_policy)
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,42 @@
1
+ require 'bundler/setup'
2
+ Bundler.require(:development)
3
+
4
+ require 'coveralls'
5
+ Coveralls.wear! unless ENV["SIMPLE_COVERAGE"]
6
+
7
+ begin
8
+ if ENV["SIMPLE_COVERAGE"]
9
+ require 'simplecov'
10
+ SimpleCov.start do
11
+ add_group "Lib", "lib"
12
+
13
+ add_filter "/spec/"
14
+ end
15
+ end
16
+ rescue LoadError
17
+ warn "=" * 80
18
+ warn 'simplecov not installed. No coverage report'
19
+ warn "=" * 80
20
+ end
21
+
22
+ require 'access_policy'
23
+
24
+ Dir[File.join(File.expand_path(__dir__ ), "support/**/*.rb")].each { |f| require f }
25
+
26
+ # This file was generated by the `rspec --init` command. Conventionally, all
27
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
28
+ # Require this file using `require "spec_helper"` to ensure that it is only
29
+ # loaded once.
30
+ #
31
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
32
+ RSpec.configure do |config|
33
+ config.treat_symbols_as_metadata_keys_with_true_values = true
34
+ config.run_all_when_everything_filtered = true
35
+ config.filter_run :focus
36
+
37
+ # Run specs in random order to surface order dependencies. If you find an
38
+ # order dependency and want to debug it, you can fix the order by providing
39
+ # the seed, which is printed after each run.
40
+ # --seed 1234
41
+ config.order = 'random'
42
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+ require 'access_policy/rspec_matchers'
3
+
4
+ describe 'PolicyMatchers' do
5
+ context 'none policy spec' do
6
+ it 'has no access to permit' do
7
+ expect { permit }.to raise_error(NameError)
8
+ end
9
+
10
+ it 'has no access to forbid' do
11
+ expect { forbid }.to raise_error(NameError)
12
+ end
13
+ end
14
+
15
+ context 'policy spec', type: :policy do
16
+ it 'grands access to permit' do
17
+ expect { permit }.not_to raise_error
18
+ end
19
+
20
+ it 'grands access to forbid' do
21
+ expect { forbid }.not_to raise_error
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,133 @@
1
+ require 'spec_helper'
2
+
3
+ module PolicyCheckSpec
4
+ class Dummy
5
+
6
+ end
7
+
8
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
9
+ def call?
10
+ true
11
+ end
12
+ end
13
+ end
14
+
15
+ module AccessPolicy
16
+ describe PolicyCheck do
17
+ subject {
18
+ AccessPolicy::PolicyCheck.new.tap { |p|
19
+ p.current_user_or_role_for_policy=nil
20
+ p.policy_authorized = true
21
+ }
22
+
23
+ }
24
+
25
+ let(:current_user) {
26
+ "It's me"
27
+ }
28
+
29
+
30
+ describe '.policy_for' do
31
+
32
+ it 'returns a policy object for a class' do
33
+ expect(subject.policy_for(PolicyCheckSpec::Dummy)).to be_kind_of PolicyCheckSpec::DummyPolicy
34
+ end
35
+
36
+ it 'returns a policy object for a object' do
37
+ expect(subject.policy_for(PolicyCheckSpec::Dummy.new)).to be_kind_of PolicyCheckSpec::DummyPolicy
38
+ end
39
+
40
+ it 'sets the current user' do
41
+ subject.current_user_or_role_for_policy = current_user
42
+ expect(subject.policy_for(PolicyCheckSpec::Dummy.new).current_user).to be current_user
43
+ end
44
+
45
+ it 'uses my error handler' do
46
+ object_to_guard = Object.new
47
+ error_handler=double('error handler')
48
+ expect(error_handler).to receive(:call).with(object_to_guard)
49
+
50
+ subject.policy_for(object_to_guard, error_handler)
51
+ end
52
+ end
53
+
54
+ describe '.with_user_or_role' do
55
+ it 'sets the current user' do
56
+ subject.with_user_or_role(current_user) do
57
+ subject.policy_authorized = true
58
+ expect(subject.current_user_or_role_for_policy).to eq current_user
59
+ end
60
+ end
61
+
62
+ it 'sets the current user back after execution' do
63
+ subject.current_user_or_role_for_policy = 'other user'
64
+
65
+ subject.with_user_or_role(current_user) do
66
+ subject.policy_authorized = true
67
+ end
68
+
69
+ expect(subject.current_user_or_role_for_policy).to eq 'other user'
70
+ end
71
+
72
+ it 'raises PolicyEnforcer::NotAuthorizedError when authorize is not called' do
73
+ expect { subject.with_user_or_role(current_user) }.to raise_error PolicyEnforcer::NotAuthorizedError
74
+ end
75
+
76
+ it 'does not raise PolicyEnforcer::NotAuthorizedError when authorize is called' do
77
+ expect {
78
+ subject.with_user_or_role(current_user) do
79
+ subject.authorize(PolicyCheckSpec::Dummy.new, "call")
80
+ end
81
+ }.not_to raise_error
82
+ end
83
+
84
+ it 'uses an custom an error handler' do
85
+ error_handler=double('error handler')
86
+ expect(error_handler).to receive(:call).with(kind_of(StandardError))
87
+
88
+ subject.with_user_or_role(current_user, error_handler)
89
+ end
90
+
91
+ it 'supports nested context switches' do
92
+ user1 = 'user 1'
93
+ user2 = 'user 2'
94
+ user3 = 'user 3'
95
+
96
+ subject.current_user_or_role_for_policy = user1
97
+
98
+ subject.with_user_or_role(user2, ->(*){}) do
99
+ expect(subject.current_user_or_role_for_policy).to eq user2
100
+
101
+ subject.with_user_or_role(user3) do
102
+ expect(subject.current_user_or_role_for_policy).to eq user3
103
+ end
104
+
105
+ expect(subject.current_user_or_role_for_policy).to eq user2
106
+ end
107
+
108
+ expect(subject.current_user_or_role_for_policy).to eq user1
109
+
110
+ end
111
+
112
+ end
113
+
114
+ describe '.authorize' do
115
+ it 'returns true when the user is authorized' do
116
+ expect(subject.authorize(PolicyCheckSpec::Dummy.new, "call")).to be == true
117
+ end
118
+
119
+ it 'raises an exception when no method is defined' do
120
+ expect { subject.authorize(PolicyCheckSpec::Dummy.new, "call2") }.to raise_error
121
+ end
122
+
123
+ it 'uses a given error handler' do
124
+ object_to_guard = PolicyCheckSpec::Dummy.new
125
+ error_handler=double('error handler')
126
+ expect(error_handler).to receive(:call).with(object_to_guard)
127
+
128
+ subject.authorize(object_to_guard, "call2", error_policy: error_handler)
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -0,0 +1,135 @@
1
+ require 'spec_helper'
2
+
3
+ module PolicyEnforcerSpec
4
+ class Dummy
5
+
6
+ end
7
+
8
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
9
+ def call?
10
+ true
11
+ end
12
+
13
+ def not_allowed_call?
14
+ false
15
+ end
16
+ end
17
+
18
+ module A
19
+ class Dummy
20
+
21
+ end
22
+
23
+ DummyPolicy = Struct.new(:d_current_user, :d_object_to_guard)
24
+ end
25
+ end
26
+
27
+ module AccessPolicy
28
+ describe PolicyEnforcer do
29
+ subject {
30
+ AccessPolicy::PolicyEnforcer.new(current_user, object_to_guard, action, error_policy)
31
+ }
32
+
33
+ let(:current_user) {
34
+ double('user')
35
+ }
36
+
37
+ let(:object_to_guard) {
38
+ PolicyEnforcerSpec::Dummy.new
39
+ }
40
+
41
+ let(:action) {
42
+ "call"
43
+ }
44
+
45
+ let(:error_policy) {
46
+ ->(*) { raise }
47
+ }
48
+
49
+ describe '#new' do
50
+ it 'raises an error when class to guard has no name' do
51
+ expect{AccessPolicy::PolicyEnforcer.new(current_user, Class.new.new, action, error_policy)}.to raise_error
52
+ end
53
+ end
54
+
55
+
56
+ describe '.policy' do
57
+
58
+ it 'returns a policy object for a class' do
59
+ subject.object_or_class = PolicyEnforcerSpec::Dummy
60
+ expect(subject.policy).to be_kind_of PolicyEnforcerSpec::DummyPolicy
61
+ end
62
+
63
+ it 'returns a policy object for a object' do
64
+ expect(subject.policy).to be_kind_of PolicyEnforcerSpec::DummyPolicy
65
+ end
66
+
67
+ it 'sets the current user' do
68
+ expect(subject.policy.current_user).to be current_user
69
+ end
70
+
71
+ it 'uses my error handler' do
72
+ object_to_guard = Object.new
73
+ error_handler=double('error handler')
74
+ expect(error_handler).to receive(:call).with(object_to_guard)
75
+
76
+ subject.object_or_class = object_to_guard
77
+
78
+ subject.policy(error_handler)
79
+ end
80
+
81
+
82
+ it 'returns a policy object for a class within a module' do
83
+ subject.object_or_class = PolicyEnforcerSpec::A::Dummy.new
84
+ expect(subject.policy).to be_kind_of PolicyEnforcerSpec::A::DummyPolicy
85
+ end
86
+
87
+ it 'raises Policy::NotDefinedError when no policy class found' do
88
+ B = Object
89
+ subject.object_or_class = B
90
+ expect { subject.policy }.to raise_error PolicyEnforcer::NotDefinedError
91
+ end
92
+
93
+ it 'raises Policy::NotDefinedError when class.name is empty' do
94
+ subject.object_or_class = Class.new
95
+ expect { subject.policy }.to raise_error PolicyEnforcer::NotDefinedError
96
+ end
97
+ end
98
+
99
+ describe '.authorize' do
100
+ it 'returns true when the user is authorized' do
101
+ expect(subject.authorize).to be == true
102
+ end
103
+
104
+ it 'raises an exception when the user is not authorized' do
105
+ subject.query = "not_allowed_call"
106
+ expect{subject.authorize}.to raise_error
107
+ end
108
+
109
+ it 'raises an exception when no method is defined' do
110
+ subject.query = "call2"
111
+ expect { subject.authorize }.to raise_error PolicyEnforcer::NotDefinedError
112
+ end
113
+
114
+ it 'uses a given error handler' do
115
+ object_to_guard = Object.new
116
+ error_handler=double('error handler')
117
+ expect(error_handler).to receive(:call).with(object_to_guard)
118
+
119
+ subject.object_or_class = object_to_guard
120
+
121
+ subject.authorize(error_handler)
122
+ end
123
+
124
+ it 'yields when authorize was successful' do
125
+ expect { |block| subject.authorize(&block) }.to yield_with_args(true)
126
+ end
127
+
128
+ it 'does not yield when authorize was not successful' do
129
+ subject.query = "call2"
130
+ expect { |block| subject.authorize(->(*) {}, &block) }.not_to yield_with_args
131
+ end
132
+ end
133
+
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,252 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: access_policy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Dieter Späth
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: scoped_storage
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
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 2.99.0.beta1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 2.99.0.beta1
69
+ - !ruby/object:Gem::Dependency
70
+ name: fuubar
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-remote
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: guard
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: guard-rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: guard-bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rb-fsevent
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: pry-stack_explorer
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: pry-debugger
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ description: Object oriented authorization for ruby.
196
+ email:
197
+ - shad0wrunner@gmx.de
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files: []
201
+ files:
202
+ - ".gitignore"
203
+ - ".rspec"
204
+ - ".travis.yml"
205
+ - Gemfile
206
+ - Guardfile
207
+ - LICENSE.txt
208
+ - README.md
209
+ - Rakefile
210
+ - access_policy.gemspec
211
+ - lib/access_policy.rb
212
+ - lib/access_policy/policy_check.rb
213
+ - lib/access_policy/policy_enforcer.rb
214
+ - lib/access_policy/rspec_matchers.rb
215
+ - lib/access_policy/version.rb
216
+ - spec/acceptance/matcher_example_spec.rb
217
+ - spec/integration/lib/access_policy_spec.rb
218
+ - spec/spec_helper.rb
219
+ - spec/unit/lib/access_policy/matcher_spec.rb
220
+ - spec/unit/lib/access_policy/policy_check_spec.rb
221
+ - spec/unit/lib/access_policy/policy_enforcer_spec.rb
222
+ homepage: ''
223
+ licenses:
224
+ - MIT
225
+ metadata: {}
226
+ post_install_message:
227
+ rdoc_options: []
228
+ require_paths:
229
+ - lib
230
+ required_ruby_version: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: '0'
235
+ required_rubygems_version: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - ">="
238
+ - !ruby/object:Gem::Version
239
+ version: '0'
240
+ requirements: []
241
+ rubyforge_project:
242
+ rubygems_version: 2.2.1
243
+ signing_key:
244
+ specification_version: 4
245
+ summary: Object oriented authorization for ruby.
246
+ test_files:
247
+ - spec/acceptance/matcher_example_spec.rb
248
+ - spec/integration/lib/access_policy_spec.rb
249
+ - spec/spec_helper.rb
250
+ - spec/unit/lib/access_policy/matcher_spec.rb
251
+ - spec/unit/lib/access_policy/policy_check_spec.rb
252
+ - spec/unit/lib/access_policy/policy_enforcer_spec.rb