access_policy 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d6fec0043cb9fd0e0e318c74c3a123d5928373e
4
- data.tar.gz: 416b4171c59eab9dd40e2235e9059cd9a9b8c914
3
+ metadata.gz: c13ac5c6dac57c9f9941984ef3a3cdfb41ea0a2e
4
+ data.tar.gz: 72b632f8163d91a4655bb8b9fdd4989402c76777
5
5
  SHA512:
6
- metadata.gz: aa415b1b7d75b12278d6c0edc38d79d2558c912a44d3486b2f3a719fa01bee235eeb80716a84a78e4aa00894614a9ac62cfcf3a72802cbcf27790a891be12780
7
- data.tar.gz: 51a51cc18979bed2caf2795cdab657a3137bfb4eae275d2385b2af84c9f0a991332d175482580c49819560f976b5be03d8c04613e00b5a09854e32cb1b597727
6
+ metadata.gz: 08b09203d8dfeda012749a7b6480ecbc594120cf16df57b92734f06eb8fbd5501a9429c5c836670bef0fd6a73e361b28482540c962b9a1aec6bd8986d287f029
7
+ data.tar.gz: 12c95f04b081718da2b910b68d89942514862d59fdc44ad240c86f30d14273c9e28be567feb6915edf769d6ee9453ec68ecf29105cb7a6e47db17248443f8e34
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # AccessPolicy
1
+ # AccessPolicy [![Code Climate](https://codeclimate.com/github/slowjack2k/access_policy.png)](https://codeclimate.com/github/slowjack2k/access_policy) [![Build Status](https://travis-ci.org/slowjack2k/access_policy.png?branch=master)](https://travis-ci.org/slowjack2k/access_policy) [![Coverage Status](https://coveralls.io/repos/slowjack2k/access_policy/badge.png?branch=master)](https://coveralls.io/r/slowjack2k/access_policy?branch=master) [![Gem Version](https://badge.fury.io/rb/access_policy.png)](http://badge.fury.io/rb/access_policy)
2
2
 
3
3
  Object oriented authorization for ruby. It provides helper to
4
4
  protect method call's via Policy-Classes.
@@ -32,8 +32,15 @@ end
32
32
 
33
33
  ToGuardPolicy = Struct.new(:current_user_or_role, :object_of_kind_to_guard) do
34
34
 
35
+ attr_accessor :error_message # optional
36
+
35
37
  def method_to_guard?
36
- current_user_or_role.is_allowed?
38
+ if current_user_or_role.is_allowed?
39
+ true
40
+ else
41
+ self.error_message = "you'r not allowed to do this" # optional
42
+ false
43
+ end
37
44
  end
38
45
 
39
46
  end
@@ -48,9 +55,9 @@ policy_checker.with_user_or_role(current_user) do
48
55
  begin
49
56
  policy_checker.authorize(object_to_guard, 'method_to_guard')
50
57
  object_to_guard.method_to_guard
51
- rescue AccessPolicy::PolicyEnforcer::NotAuthorizedError
58
+ rescue AccessPolicy::NotAuthorizedError
52
59
  ...
53
- rescue AccessPolicy::PolicyEnforcer::NotDefinedError
60
+ rescue AccessPolicy::NotDefinedError
54
61
  ...
55
62
  end
56
63
  end
@@ -84,9 +91,9 @@ Or
84
91
  object_to_guard.with_user_or_role(current_user) do
85
92
  begin
86
93
  object_to_guard.method_to_guard
87
- rescue PolicyEnforcer::NotAuthorizedError
94
+ rescue AccessPolicy::NotAuthorizedError
88
95
  ...
89
- rescue PolicyEnforcer::NotDefinedError
96
+ rescue AccessPolicy::NotDefinedError
90
97
  ...
91
98
  end
92
99
  end
@@ -99,14 +106,83 @@ Or
99
106
  object_to_guard.method_to_guard_for_root
100
107
  end
101
108
 
102
- rescue PolicyEnforcer::NotAuthorizedError
109
+ rescue AccessPolicy::NotAuthorizedError
103
110
  ...
104
- rescue PolicyEnforcer::NotDefinedError
111
+ rescue AccessPolicy::NotDefinedError
105
112
  ...
106
113
  end
107
114
  end
108
115
 
109
116
 
117
+ ```
118
+
119
+ Test policies with rspec:
120
+
121
+ ```ruby
122
+
123
+ require 'spec_helper'
124
+ require 'access_policy/rspec_matchers'
125
+
126
+ module MatcherExampleSpec
127
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
128
+ def read?
129
+ true
130
+ end
131
+
132
+ def write?
133
+ current_user && current_user.allowed?
134
+ end
135
+
136
+ def destroy?
137
+ write? && object_to_guard && !object_to_guard.locked?
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "PolicyMatchers", type: :policy do
143
+ subject(:policy){
144
+ MatcherExampleSpec::DummyPolicy
145
+ }
146
+
147
+
148
+ context 'for a visitor' do
149
+ let(:visitor){nil}
150
+
151
+ it 'permits read' do
152
+ expect(policy).to permit(:read).to(visitor)
153
+ end
154
+
155
+ it 'forbids write' do
156
+ expect(policy).not_to permit(:write).to(visitor)
157
+ end
158
+ end
159
+
160
+ context 'for a admin' do
161
+ let(:admin){double('admin', allowed?: true)}
162
+ let(:locked_object_to_guard){double('object to guard', locked?: true)}
163
+ let(:unlocked_object_to_guard){double('object to guard', locked?: false)}
164
+
165
+ it 'permits read' do
166
+ expect(policy).to permit(:read).to(admin)
167
+ end
168
+
169
+ it 'permits write' do
170
+ expect(policy).to permit(:write).to(admin)
171
+ end
172
+
173
+ it 'permits destroy for unlocked objects' do
174
+ expect(policy).to permit(:destroy).on(unlocked_object_to_guard).to(admin)
175
+ end
176
+
177
+ it 'forbids destroy for locked objects' do
178
+ expect(policy).to forbid(:destroy).on(locked_object_to_guard).to(admin)
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+
185
+
110
186
  ```
111
187
 
112
188
  ## Contributing
@@ -14,7 +14,7 @@ module AccessPolicy
14
14
 
15
15
  def authorize(object_to_guard, action_to_guard, error_policy: default_error_policy)
16
16
  PolicyEnforcer.new(current_user_or_role_for_policy, object_to_guard, action_to_guard).authorize(error_policy) do
17
- self.policy_authorized=true
17
+ self.policy_authorize_called=true
18
18
  end
19
19
  end
20
20
 
@@ -23,12 +23,12 @@ module AccessPolicy
23
23
  end
24
24
 
25
25
  def with_user_or_role(new_current_user_or_role_for_policy, error_policy = default_error_policy)
26
- self.policy_authorized = false
26
+ self.policy_authorize_called = false
27
27
 
28
28
  switched_user_or_role(new_current_user_or_role_for_policy) do
29
29
  begin
30
30
  yield if block_given?
31
- raise(PolicyEnforcer::NotAuthorizedError, "#{new_current_user_or_role_for_policy}") unless policy_authorized?
31
+ raise(AccessPolicy::AuthorizeNotCalledError, "#{new_current_user_or_role_for_policy}") unless policy_authorize_called?
32
32
  rescue => e
33
33
  error_policy.call(e)
34
34
  end
@@ -43,18 +43,18 @@ module AccessPolicy
43
43
  scope['current_user_or_role_for_policy']
44
44
  end
45
45
 
46
- def policy_authorized=(new_value)
47
- scope['policy_authorized'] = new_value
46
+ def policy_authorize_called=(new_value)
47
+ scope['policy_authorize_called'] = new_value
48
48
  end
49
49
 
50
- def policy_authorized?
51
- !!policy_authorized
50
+ def policy_authorize_called?
51
+ !!policy_authorize_called
52
52
  end
53
53
 
54
54
  protected
55
55
 
56
- def policy_authorized
57
- scope['policy_authorized']
56
+ def policy_authorize_called
57
+ scope['policy_authorize_called']
58
58
  end
59
59
 
60
60
  def scope
@@ -1,9 +1,5 @@
1
1
  module AccessPolicy
2
2
  class PolicyEnforcer
3
- class NotDefinedError < StandardError;
4
- end
5
- class NotAuthorizedError < StandardError;
6
- end
7
3
 
8
4
  attr_accessor :current_user_or_role, :object_or_class, :query, :default_error_policy
9
5
 
@@ -18,7 +14,10 @@ module AccessPolicy
18
14
  end
19
15
 
20
16
  def authorize(error_policy=default_error_policy)
21
- raise(PolicyEnforcer::NotAuthorizedError, "not allowed to #{query} this #{object_or_class}" ) unless _guard_action()
17
+ unless _guard_action()
18
+ error_message = policy.respond_to?(:error_message) ? policy.error_message : "not allowed to #{query} this #{object_or_class}"
19
+ raise(AccessPolicy::NotAuthorizedError, error_message)
20
+ end
22
21
  yield true if block_given?
23
22
  true
24
23
  rescue
@@ -26,7 +25,7 @@ module AccessPolicy
26
25
  end
27
26
 
28
27
  def policy(error_policy=default_error_policy)
29
- specific_policy_for_class.new(current_user_or_role, object_or_class)
28
+ @policy||= specific_policy_for_class.new(current_user_or_role, object_or_class)
30
29
  rescue
31
30
  error_policy.call(object_or_class)
32
31
  end
@@ -2,75 +2,103 @@ module AccessPolicy
2
2
  module RspecMatchers
3
3
  extend ::RSpec::Matchers::DSL
4
4
 
5
+
5
6
  def self.included(base)
6
7
  base.metadata[:type] = :policy
7
8
  end
8
9
 
9
- module Common
10
- def self.call(base)
11
- base.instance_eval do
12
- chain :to do |user|
13
- @user = user
14
- end
10
+ class PositivePolicyMatcher
11
+ attr_accessor :policy_class, :user, :object_to_guard, :permission
12
+
13
+ def initialize(permission)
14
+ self.permission = permission
15
+ end
16
+
17
+ def matches?(policy_class)
18
+ self.policy_class = policy_class
19
+ eval_match
20
+ end
15
21
 
16
- chain :on do |object_to_guard|
17
- @object_to_guard = object_to_guard
18
- end
22
+ def eval_match
23
+ permission_granted?
24
+ end
19
25
 
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
26
+ def failure_message
27
+ "#{policy_class} #{failure_message_part} '#{permission}'#{object_as_text}#{user_as_text}."
28
+ end
24
29
 
25
- define_method :permission_denied? do |*args|
26
- ! permission_granted?(*args)
27
- end
30
+ def negative_failure_message
31
+ "#{policy_class} #{negative_failure_message_part} '#{permission}'#{object_as_text}#{user_as_text}."
32
+ end
28
33
 
29
- define_method :object_as_text do
30
- @object_to_guard.nil? ? '' : " on #{@object_to_guard.inspect}"
31
- end
32
34
 
33
- define_method :user_as_text do
34
- @user.nil? ? '' : " for #{@user.inspect}"
35
- end
35
+ def failure_message_part
36
+ 'does not permit'
37
+ end
36
38
 
37
- end
39
+ def negative_failure_message_part
40
+ 'does not forbid'
38
41
  end
39
42
 
40
- end
43
+ def to(user)
44
+ self.user = user
45
+ self
46
+ end
47
+ alias_method :for, :to
48
+ alias_method :for_user, :to
49
+ alias_method :to_user, :to
41
50
 
42
- matcher :permit do |permission|
43
- match do |policy_class|
44
- permission_granted?(policy_class, permission)
51
+ def on(object_to_guard)
52
+ self.object_to_guard = object_to_guard
53
+ self
45
54
  end
55
+ alias_method :with, :on
56
+
57
+ protected
46
58
 
47
- failure_message_for_should do |policy_class|
48
- "#{policy_class} does not permit #{permission} #{object_as_text}#{user_as_text}."
59
+ def permission_granted?
60
+ policy = policy_class.new(@user, @object_to_guard)
61
+ policy.public_send("#{permission}?")
49
62
  end
50
63
 
51
- failure_message_for_should_not do |policy_class|
52
- "#{policy_class} does not forbid #{permission}#{object_as_text}#{user_as_text}."
64
+ def object_as_text
65
+ self.object_to_guard.nil? ? '' : " on #{self.object_to_guard.inspect}"
53
66
  end
54
67
 
55
- Common.call(self)
68
+ def user_as_text
69
+ self.user.nil? ? '' : " to #{self.user.inspect}"
70
+ end
56
71
 
57
72
  end
58
73
 
59
- matcher :forbid do |permission|
60
- match do |policy_class|
61
- permission_denied?(policy_class, permission)
74
+ class NegativePolicyMatcher < PositivePolicyMatcher
75
+ def eval_match
76
+ permission_denied?
77
+ end
78
+
79
+ def failure_message_part
80
+ 'does not forbid'
62
81
  end
63
82
 
64
- failure_message_for_should do |policy_class|
65
- "#{policy_class} does not forbid #{permission} #{object_as_text}#{user_as_text}."
83
+ def negative_failure_message_part
84
+ 'does permit'
66
85
  end
67
86
 
68
- failure_message_for_should_not do |policy_class|
69
- "#{policy_class} does not permit #{permission}#{object_as_text}#{user_as_text}."
87
+ protected
88
+
89
+ def permission_denied?
90
+ ! permission_granted?
70
91
  end
92
+ end
71
93
 
72
- Common.call(self)
94
+ def permit(expected=nil)
95
+ PositivePolicyMatcher.new(expected)
73
96
  end
97
+
98
+ def forbid(expected=nil)
99
+ NegativePolicyMatcher.new(expected)
100
+ end
101
+
74
102
  end
75
103
  end
76
104
 
@@ -1,3 +1,3 @@
1
1
  module AccessPolicy
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/access_policy.rb CHANGED
@@ -6,6 +6,15 @@ require 'access_policy/policy_enforcer'
6
6
 
7
7
  module AccessPolicy
8
8
 
9
+ class NotDefinedError < StandardError
10
+ end
11
+
12
+ class NotAuthorizedError < StandardError
13
+ end
14
+
15
+ class AuthorizeNotCalledError < StandardError
16
+ end
17
+
9
18
  def self.included(base)
10
19
  base.extend ClassMethods
11
20
  end
@@ -40,8 +40,10 @@ describe AccessPolicy do
40
40
 
41
41
  it 'protects created methods' do
42
42
  expect {
43
- subject.call
44
- }.to raise_error AccessPolicy::PolicyEnforcer::NotAuthorizedError
43
+ subject.with_user_or_role(falsy_user) do
44
+ subject.call
45
+ end
46
+ }.to raise_error AccessPolicy::NotAuthorizedError
45
47
  end
46
48
 
47
49
  it 'grands access to guarded methods when the user has the right' do
@@ -17,7 +17,7 @@ module AccessPolicy
17
17
  subject {
18
18
  AccessPolicy::PolicyCheck.new.tap { |p|
19
19
  p.current_user_or_role_for_policy=nil
20
- p.policy_authorized = true
20
+ p.policy_authorize_called = true
21
21
  }
22
22
 
23
23
  }
@@ -54,7 +54,7 @@ module AccessPolicy
54
54
  describe '.with_user_or_role' do
55
55
  it 'sets the current user' do
56
56
  subject.with_user_or_role(current_user) do
57
- subject.policy_authorized = true
57
+ subject.policy_authorize_called = true
58
58
  expect(subject.current_user_or_role_for_policy).to eq current_user
59
59
  end
60
60
  end
@@ -63,14 +63,14 @@ module AccessPolicy
63
63
  subject.current_user_or_role_for_policy = 'other user'
64
64
 
65
65
  subject.with_user_or_role(current_user) do
66
- subject.policy_authorized = true
66
+ subject.policy_authorize_called = true
67
67
  end
68
68
 
69
69
  expect(subject.current_user_or_role_for_policy).to eq 'other user'
70
70
  end
71
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
72
+ it 'raises PolicyEnforcer::AuthorizeNotCalledError when authorize is not called' do
73
+ expect { subject.with_user_or_role(current_user) }.to raise_error AccessPolicy::AuthorizeNotCalledError
74
74
  end
75
75
 
76
76
  it 'does not raise PolicyEnforcer::NotAuthorizedError when authorize is called' do
@@ -13,6 +13,10 @@ module PolicyEnforcerSpec
13
13
  def not_allowed_call?
14
14
  false
15
15
  end
16
+
17
+ def error_message
18
+ "some thing went wrong"
19
+ end
16
20
  end
17
21
 
18
22
  module A
@@ -87,12 +91,12 @@ module AccessPolicy
87
91
  it 'raises Policy::NotDefinedError when no policy class found' do
88
92
  B = Object
89
93
  subject.object_or_class = B
90
- expect { subject.policy }.to raise_error PolicyEnforcer::NotDefinedError
94
+ expect { subject.policy }.to raise_error AccessPolicy::NotDefinedError
91
95
  end
92
96
 
93
97
  it 'raises Policy::NotDefinedError when class.name is empty' do
94
98
  subject.object_or_class = Class.new
95
- expect { subject.policy }.to raise_error PolicyEnforcer::NotDefinedError
99
+ expect { subject.policy }.to raise_error AccessPolicy::NotDefinedError
96
100
  end
97
101
  end
98
102
 
@@ -106,9 +110,14 @@ module AccessPolicy
106
110
  expect{subject.authorize}.to raise_error
107
111
  end
108
112
 
113
+ it 'sets custom error messages on the error' do
114
+ subject.query = "not_allowed_call"
115
+ expect{subject.authorize}.to raise_error(AccessPolicy::NotAuthorizedError, "some thing went wrong")
116
+ end
117
+
109
118
  it 'raises an exception when no method is defined' do
110
119
  subject.query = "call2"
111
- expect { subject.authorize }.to raise_error PolicyEnforcer::NotDefinedError
120
+ expect { subject.authorize }.to raise_error AccessPolicy::NotDefinedError
112
121
  end
113
122
 
114
123
  it 'uses a given error handler' do
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+ require 'access_policy/rspec_matchers'
3
+
4
+ module RspecMatchersSpec
5
+ DummyPolicy = Struct.new(:current_user, :object_to_guard) do
6
+ def read?
7
+ true
8
+ end
9
+
10
+ def write?
11
+ false
12
+ end
13
+ end
14
+ end
15
+
16
+ describe 'PolicyMatchers' do
17
+ context 'none policy spec' do
18
+ it 'has no access to permit' do
19
+ expect { permit }.to raise_error(NameError)
20
+ end
21
+
22
+ it 'has no access to forbid' do
23
+ expect { forbid }.to raise_error(NameError)
24
+ end
25
+ end
26
+
27
+ context 'policy spec', type: :policy do
28
+ it 'grands access to permit' do
29
+ expect { permit }.not_to raise_error
30
+ end
31
+
32
+ it 'grands access to forbid' do
33
+ expect { forbid }.not_to raise_error
34
+ end
35
+
36
+
37
+ context 'permit' do
38
+ let(:record){'a record'}
39
+ let(:user){'a user'}
40
+
41
+ it 'returns true when permission in granted' do
42
+ matcher = permit(:read).on(record).to(user)
43
+ expect(matcher.matches?(RspecMatchersSpec::DummyPolicy)).to be_truthy
44
+ end
45
+
46
+ it 'sets error messages' do
47
+ matcher = permit(:read).on(record).to(user)
48
+ matcher.matches?(RspecMatchersSpec::DummyPolicy)
49
+
50
+ expect(matcher.failure_message).to eq %q{RspecMatchersSpec::DummyPolicy does not permit 'read' on "a record" to "a user".}
51
+ expect(matcher.negative_failure_message).to eq %q{RspecMatchersSpec::DummyPolicy does not forbid 'read' on "a record" to "a user".}
52
+ end
53
+
54
+ it 'returns false when permission is not granted' do
55
+ matcher = permit(:write).on(record).to(user)
56
+ expect(matcher.matches?(RspecMatchersSpec::DummyPolicy)).to be_falsy
57
+ end
58
+ end
59
+
60
+ context 'forbid' do
61
+ let(:record){'a record'}
62
+ let(:user){'a user'}
63
+
64
+ it 'returns false when permission in granted' do
65
+ matcher = forbid(:read).on(record).to(user)
66
+ expect(matcher.matches?(RspecMatchersSpec::DummyPolicy)).to be_falsy
67
+ end
68
+
69
+ it 'returns true when permission is not granted' do
70
+ matcher = forbid(:write).on(record).to(user)
71
+ expect(matcher.matches?(RspecMatchersSpec::DummyPolicy)).to be_truthy
72
+ end
73
+
74
+ it 'sets error messages' do
75
+ matcher = forbid(:read).on(record).to(user)
76
+ matcher.matches?(RspecMatchersSpec::DummyPolicy)
77
+
78
+ expect(matcher.failure_message).to eq %q{RspecMatchersSpec::DummyPolicy does not forbid 'read' on "a record" to "a user".}
79
+ expect(matcher.negative_failure_message).to eq %q{RspecMatchersSpec::DummyPolicy does permit 'read' on "a record" to "a user".}
80
+ end
81
+ end
82
+ end
83
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: access_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dieter Späth
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-11 00:00:00.000000000 Z
11
+ date: 2014-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: scoped_storage
@@ -216,9 +216,9 @@ files:
216
216
  - spec/acceptance/matcher_example_spec.rb
217
217
  - spec/integration/lib/access_policy_spec.rb
218
218
  - spec/spec_helper.rb
219
- - spec/unit/lib/access_policy/matcher_spec.rb
220
219
  - spec/unit/lib/access_policy/policy_check_spec.rb
221
220
  - spec/unit/lib/access_policy/policy_enforcer_spec.rb
221
+ - spec/unit/lib/access_policy/rspec_matchers_spec.rb
222
222
  homepage: ''
223
223
  licenses:
224
224
  - MIT
@@ -247,6 +247,6 @@ test_files:
247
247
  - spec/acceptance/matcher_example_spec.rb
248
248
  - spec/integration/lib/access_policy_spec.rb
249
249
  - spec/spec_helper.rb
250
- - spec/unit/lib/access_policy/matcher_spec.rb
251
250
  - spec/unit/lib/access_policy/policy_check_spec.rb
252
251
  - spec/unit/lib/access_policy/policy_enforcer_spec.rb
252
+ - spec/unit/lib/access_policy/rspec_matchers_spec.rb
@@ -1,24 +0,0 @@
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