access_policy 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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