rspec-authorization 0.0.2 → 0.0.6

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.
@@ -21,7 +21,7 @@ module RSpec::Authorization
21
21
  # it { is_expected.to have_permission_for(:user).to(:index) }
22
22
  # end
23
23
  #
24
- # Currently this matcher only support RESTful action check, to check the
24
+ # Currently this matcher only support restful action check, to check the
25
25
  # controller against +config/authorization_rules.rb+. Skipping the +#to+
26
26
  # method will result in default action assigned as +:index+.
27
27
  #
@@ -29,36 +29,20 @@ module RSpec::Authorization
29
29
  #
30
30
  # it { is_expected.to have_permission_for(:user) }
31
31
  #
32
- # === RESTful methods
32
+ # === RESTful helper methods
33
33
  #
34
- # For your convenience, there are 4 RESTful methods available to be chained
35
- # from the matcher, which are:
36
- #
37
- # - +to_read+
38
- # - +to_create+
39
- # - +to_update+
40
- # - +to_delete+
41
- #
42
- # Consider the following example:
34
+ # For your convenience, there are restful helper methods available to be
35
+ # chained from the matcher, consider the following example:
43
36
  #
44
37
  # it { is_expected.to have_permission_for(:user).to_read }
45
- # it { is_expected.to have_permission_for(:user).to_edit }
38
+ # it { is_expected.to have_permission_for(:user).to_create }
46
39
  # it { is_expected.not_to have_permission_for(:user).to_update }
47
40
  # it { is_expected.not_to have_permission_for(:user).to_delete }
41
+ # it { is_expected.not_to have_permission_for(:user).to_manage }
48
42
  #
49
- # The above method is not related to declarative_authorization privileges,
50
- # and serve simply as convinience method, below is a table of RESTful actions
51
- # for each method mentioned above:
52
- #
53
- # Method RESTful action
54
- # ------------------------------------
55
- # to_read [:index, :show]
56
- # to_edit [:edit, :update]
57
- # to_create [:new, :create]
58
- # to_delete [:destroy]
59
- #
60
- # Matcher for RESTful methods is slightly different than that of a single
61
- # method, following is how RESTful methods request results evaluated:
43
+ # Matcher for restful helper methods is slightly different than that of
44
+ # a single method, following is an example of how restful helper methods
45
+ # request results evaluated:
62
46
  #
63
47
  # all_requests (of #to_read) matches? does_not_match?
64
48
  # -------------------------------------------------------------------
@@ -66,7 +50,48 @@ module RSpec::Authorization
66
50
  # {index: true, show: false} false false
67
51
  # {index: false, show: false} false true
68
52
  #
53
+ # === Focused RESTful helper methods
54
+ #
55
+ # There are cases where you need to focused your matching only for a given
56
+ # criteria, let's say only match for read actions, or match except delete
57
+ # action, consider the following example:
58
+ #
59
+ # it { is_expected.to have_permission_for(:user).only_to_read }
60
+ # it { is_expected.to have_permission_for(:writer).except_to_delete }
61
+ #
62
+ # The above statements have their negated counterparts, consider the
63
+ # following example:
64
+ #
65
+ # it { is_expected.not_to have_permission_for(:user).only_to_read }
66
+ # it { is_expected.not_to have_permission_for(:writer).only_to_delete }
67
+ #
68
+ # If you see the above negated matcher, they actually have a relationship
69
+ # to the other's negated counterpart instead of theirs, consider the following
70
+ # example:
71
+ #
72
+ # it { is_expected.to have_permission_for(:user).only_to_read }
73
+ # it { is_expected.not_to have_permission_for(:user).except_to_read }
74
+ #
75
+ # The above examples are doing exactly the same thing, so does the following
76
+ # example, these examples below also doing exactly the same thing and can
77
+ # be used in either case:
78
+ #
79
+ # it { is_expected.to have_permission_for(:writer).except_to_delete }
80
+ # it { is_expected.not_to have_permission_for(:writer).only_to_read }
81
+ #
82
+ # That means, the following example, is actually negating each other and
83
+ # can be used to negate your statements instead of using the negated version
84
+ # of the matcher:
85
+ #
86
+ # it { is_expected.to have_permission_for(:user).only_to_read }
87
+ # it { is_expected.to have_permission_for(:user).except_to_read }
88
+ #
89
+ # Even if you can have a negated matcher using a focused restful helper
90
+ # methods, it is better to stick with the possitive matcher, negated matcher
91
+ # can easily confuse you, and it only serves the purpose of completeness.
92
+ #
69
93
  # @param role [Symbol] role name to matched against
94
+ # @see RestfulHelperMethod
70
95
  def have_permission_for(role)
71
96
  HavePermissionFor.new(role)
72
97
  end
@@ -74,85 +99,83 @@ module RSpec::Authorization
74
99
  class HavePermissionFor # :nodoc: all
75
100
  include Adapters
76
101
 
77
- attr_reader :controller, :role, :behave, :actions, :results
102
+ attr_reader :role, :prefix, :action
103
+ attr_reader :resource, :restful_helper_method, :privilege
104
+ attr_reader :actions, :negated_actions
78
105
 
79
106
  def initialize(role)
80
107
  @role = role
108
+
109
+ @actions = [:index]
110
+ @negated_actions = []
81
111
  end
82
112
 
83
113
  def to(action)
84
- @behave = action
85
- @actions = [behave]
114
+ @prefix = :to
115
+ @action = action
116
+ @actions = [action]
86
117
 
87
118
  self
88
119
  end
89
120
 
90
- def to_read
91
- @behave = :read
92
- @actions = %i(index show)
93
-
94
- self
95
- end
121
+ def method_missing(method_name, *args, &block)
122
+ @restful_helper_method = RestfulHelperMethod.new(method_name)
96
123
 
97
- def to_create
98
- @behave = :create
99
- @actions = %i(new create)
124
+ @actions = restful_helper_method.actions
125
+ @negated_actions = restful_helper_method.negated_actions
100
126
 
101
127
  self
102
128
  end
103
129
 
104
- def to_update
105
- @behave = :update
106
- @actions = %i(edit update)
130
+ def matches?(controller)
131
+ build_resource(controller)
107
132
 
108
- self
133
+ resource.run_all
134
+ resource.permitted?
109
135
  end
110
136
 
111
- def to_delete
112
- @behave = :delete
113
- @actions = %i(destroy)
137
+ def does_not_match?(controller)
138
+ build_resource(controller)
114
139
 
115
- self
140
+ resource.run_all
141
+ resource.forbidden?
116
142
  end
117
143
 
118
- def to_manage
119
- @behave = :manage
120
- @actions = %i(index show new create edit update destroy)
121
-
122
- self
144
+ def failure_message
145
+ "Expected #{common_failure_message}"
123
146
  end
124
147
 
125
- def all_requests
126
- actions.map do |action|
127
- request = Request.new(controller.class, action, role)
128
- [action, request.response.status != 403]
129
- end
148
+ def failure_message_when_negated
149
+ "Did not expect #{common_failure_message}"
130
150
  end
131
151
 
132
- def matches?(controller)
133
- @controller = controller
134
- @results = Hash[all_requests]
135
-
136
- true unless results.value? false
152
+ def description
153
+ "have permission for #{role} #{humanized_behavior}"
137
154
  end
138
155
 
139
- def does_not_match?(controller)
140
- @controller = controller
141
- @results = Hash[all_requests]
156
+ private
142
157
 
143
- true unless results.value? true
158
+ def build_resource(controller)
159
+ @privilege = Privilege.new(
160
+ actions: actions,
161
+ negated_actions: negated_actions,
162
+ controller_class: controller.class,
163
+ role: role
164
+ )
165
+
166
+ @resource = Resource.new(privilege)
144
167
  end
145
168
 
146
- def failure_message
147
- "Expected #{controller.class} to have permission for #{role} to #{behave}. #{results}"
169
+ def humanized_behavior
170
+ restful_helper_method.try(:humanize) || "#{@prefix} #{action}"
148
171
  end
149
172
 
150
- def failure_message_when_negated
151
- "Did not expect #{controller.class} to have permission for #{role} to #{behave}. #{results}"
173
+ def common_failure_message
174
+ "#{resource.controller_class} to #{description}. #{debug_results}"
152
175
  end
153
176
 
154
- def description
155
- "have permission for #{role} to #{behave}"
177
+ def debug_results
178
+ "results: #{resource.results}, negated_results: #{resource.negated_results}"
156
179
  end
157
180
  end
158
181
  end
@@ -1,5 +1,5 @@
1
1
  module RSpec
2
2
  module Authorization
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.6"
4
4
  end
5
5
  end
@@ -18,8 +18,9 @@ Gem::Specification.new do |spec|
18
18
  spec.require_paths = ["lib"]
19
19
 
20
20
  spec.add_runtime_dependency "declarative_authorization"
21
- spec.add_runtime_dependency "rspec-rails", "~> 3.0"
21
+ spec.add_runtime_dependency "rspec-rails", "~> 3.0", "< 3.2"
22
22
 
23
+ spec.add_development_dependency "appraisal"
23
24
  spec.add_development_dependency "bundler", "~> 1.7"
24
25
  spec.add_development_dependency "rails", "~> 4.0"
25
26
  spec.add_development_dependency "rake", "~> 10.0"
@@ -7,15 +7,7 @@ describe ArticlesController do
7
7
  it { is_expected.not_to have_permission_for(:user).to_update }
8
8
  it { is_expected.not_to have_permission_for(:user).to_delete }
9
9
 
10
- it { is_expected.to have_permission_for(:premium).to_read }
11
- it { is_expected.not_to have_permission_for(:premium).to_create }
12
- it { is_expected.not_to have_permission_for(:premium).to_update }
13
- it { is_expected.not_to have_permission_for(:premium).to_delete }
14
-
15
- it { is_expected.to have_permission_for(:writer).to_read }
16
- it { is_expected.to have_permission_for(:writer).to_create }
17
- it { is_expected.not_to have_permission_for(:writer).to_update }
18
- it { is_expected.not_to have_permission_for(:writer).to_delete }
19
-
20
- it { is_expected.to have_permission_for(:editor).to_manage }
10
+ it { is_expected.to have_permission_for(:premium).only_to_read }
11
+ it { is_expected.to have_permission_for(:writer ).except_to_delete }
12
+ it { is_expected.to have_permission_for(:editor ).to_manage }
21
13
  end
@@ -0,0 +1,102 @@
1
+ require 'rails_helper'
2
+
3
+ include RSpec::Authorization::Adapters
4
+
5
+ describe Resource do
6
+ let(:privilege) { :privilege }
7
+ let(:resource) { Resource.new(privilege) }
8
+
9
+ subject { resource }
10
+
11
+ its(:privilege) { is_expected.to eq privilege }
12
+
13
+ specify { is_expected.to delegate_method(:actions).to(:privilege) }
14
+ specify { is_expected.to delegate_method(:negated_actions).to(:privilege) }
15
+ specify { is_expected.to delegate_method(:controller_class).to(:privilege) }
16
+ specify { is_expected.to delegate_method(:role).to(:privilege) }
17
+
18
+ describe "#run_all" do
19
+ before do
20
+ expect(resource).to receive(:requests)
21
+ expect(resource).to receive(:negated_requests)
22
+ end
23
+
24
+ specify { resource.run_all }
25
+ end
26
+
27
+ describe "#permitted?" do
28
+ context "no negated actions" do
29
+ before { allow(resource).to receive(:permitted_for?).and_return(value) }
30
+
31
+ context "permitted for actions" do
32
+ let(:value) { true }
33
+
34
+ specify { expect(resource.permitted?).to be_truthy }
35
+ end
36
+
37
+ context "not permitted for actions" do
38
+ let(:value) { false }
39
+
40
+ specify { expect(resource.permitted?).to be_falsy }
41
+ end
42
+ end
43
+
44
+ context "have negated actions" do
45
+ before do
46
+ allow(resource).to receive(:negated_results).and_return([:present])
47
+ allow(resource).to receive(:permitted_for?).and_return(permitted_value)
48
+ allow(resource).to receive(:forbidden_for?).and_return(true)
49
+ end
50
+
51
+ context "permitted for actions" do
52
+ let(:permitted_value) { true }
53
+
54
+ specify { expect(resource.permitted?).to be_truthy }
55
+ end
56
+
57
+ context "not permitted for actions" do
58
+ let(:permitted_value) { false }
59
+
60
+ specify { expect(resource.permitted?).to be_falsy }
61
+ end
62
+ end
63
+ end
64
+
65
+ describe "#forbidden?" do
66
+ context "no negated actions" do
67
+ before { allow(resource).to receive(:forbidden_for?).and_return(value) }
68
+
69
+ context "forbidden for actions" do
70
+ let(:value) { true }
71
+
72
+ specify { expect(resource.forbidden?).to be_truthy }
73
+ end
74
+
75
+ context "not forbidden for actions" do
76
+ let(:value) { false }
77
+
78
+ specify { expect(resource.forbidden?).to be_falsy }
79
+ end
80
+ end
81
+
82
+ context "have negated actions" do
83
+ before do
84
+ allow(resource).to receive(:negated_results).and_return([:present])
85
+ allow(resource).to receive(:forbidden_for?).and_return(value)
86
+ allow(resource).to receive(:permitted_for?).and_return(true)
87
+ end
88
+
89
+ context "forbidden for actions" do
90
+ let(:value) { true }
91
+
92
+ specify { expect(resource.forbidden?).to be_truthy }
93
+ end
94
+
95
+ context "not forbidden for actions" do
96
+ let(:value) { false }
97
+
98
+ specify { expect(resource.forbidden?).to be_falsy }
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,151 @@
1
+ require 'rails_helper'
2
+
3
+ include RSpec::Authorization::Adapters
4
+
5
+ describe RestfulHelperMethod do
6
+ let(:restful_helper_method) { RestfulHelperMethod.new(name) }
7
+
8
+ subject { restful_helper_method }
9
+
10
+ describe "#humanize" do
11
+ let(:name) { :only_to_read }
12
+
13
+ specify { expect(restful_helper_method.humanize).to eq "only to read" }
14
+ end
15
+
16
+ describe "#to_a" do
17
+ let(:name) { :to_read }
18
+ let(:actions) { %i(list of action from actions) }
19
+ let(:negated_actions) { %i(list of negated action from negated_actions) }
20
+
21
+ before do
22
+ allow(restful_helper_method).to receive(:actions).and_return(actions)
23
+ allow(restful_helper_method).to receive(:negated_actions).and_return(negated_actions)
24
+ end
25
+
26
+ context "implicitly infers to array" do
27
+ specify { expect([*restful_helper_method]).to eq [actions, negated_actions] }
28
+ end
29
+ end
30
+
31
+ context "method unavailable" do
32
+ specify { expect{ RestfulHelperMethod.new(:to_explode) }.to raise_error NoMethodError }
33
+ end
34
+
35
+ context "restful methods" do
36
+ let(:name) { :to_read }
37
+ its(:prefix) { is_expected.to eq :to }
38
+ its(:negated_actions) { is_expected.to eq %i() }
39
+
40
+ context "to_read" do
41
+ let(:name) { :to_read }
42
+
43
+ its(:behavior) { is_expected.to eq :read }
44
+ its(:actions) { is_expected.to eq %i(index show) }
45
+ end
46
+
47
+ context "to_create" do
48
+ let(:name) { :to_create }
49
+
50
+ its(:behavior) { is_expected.to eq :create }
51
+ its(:actions) { is_expected.to eq %i(new create) }
52
+ end
53
+
54
+ context "to_update" do
55
+ let(:name) { :to_update }
56
+
57
+ its(:behavior) { is_expected.to eq :update }
58
+ its(:actions) { is_expected.to eq %i(edit update) }
59
+ end
60
+
61
+ context "to_delete" do
62
+ let(:name) { :to_delete }
63
+
64
+ its(:behavior) { is_expected.to eq :delete }
65
+ its(:actions) { is_expected.to eq %i(destroy) }
66
+ end
67
+
68
+ context "to_manage" do
69
+ let(:name) { :to_manage }
70
+
71
+ its(:behavior) { is_expected.to eq :manage }
72
+ its(:actions) { is_expected.to eq %i(index show new create edit update destroy) }
73
+ end
74
+ end
75
+
76
+ context "focused restful methods" do
77
+ context "only_to" do
78
+ let(:name) { :only_to_read }
79
+ its(:prefix) { is_expected.to eq :only_to }
80
+
81
+ context "only_to_read" do
82
+ let(:name) { :only_to_read }
83
+
84
+ its(:behavior) { is_expected.to eq :read }
85
+ its(:actions) { is_expected.to eq %i(index show) }
86
+ its(:negated_actions) { is_expected.to eq %i(new create edit update destroy) }
87
+ end
88
+
89
+ context "only_to_create" do
90
+ let(:name) { :only_to_create }
91
+
92
+ its(:behavior) { is_expected.to eq :create }
93
+ its(:actions) { is_expected.to eq %i(new create) }
94
+ its(:negated_actions) { is_expected.to eq %i(index show edit update destroy) }
95
+ end
96
+
97
+ context "only_to_update" do
98
+ let(:name) { :only_to_update }
99
+
100
+ its(:behavior) { is_expected.to eq :update }
101
+ its(:actions) { is_expected.to eq %i(edit update) }
102
+ its(:negated_actions) { is_expected.to eq %i(index show new create destroy) }
103
+ end
104
+
105
+ context "only_to_delete" do
106
+ let(:name) { :only_to_delete }
107
+
108
+ its(:behavior) { is_expected.to eq :delete }
109
+ its(:actions) { is_expected.to eq %i(destroy) }
110
+ its(:negated_actions) { is_expected.to eq %i(index show new create edit update) }
111
+ end
112
+ end
113
+
114
+ context "except_to" do
115
+ let(:name) { :except_to_read }
116
+ its(:prefix) { is_expected.to eq :except_to }
117
+
118
+ context "except_to_read" do
119
+ let(:name) { :except_to_read }
120
+
121
+ its(:behavior) { is_expected.to eq :read }
122
+ its(:actions) { is_expected.to eq %i(new create edit update destroy) }
123
+ its(:negated_actions) { is_expected.to eq %i(index show) }
124
+ end
125
+
126
+ context "except_to_create" do
127
+ let(:name) { :except_to_create }
128
+
129
+ its(:behavior) { is_expected.to eq :create }
130
+ its(:actions) { is_expected.to eq %i(index show edit update destroy) }
131
+ its(:negated_actions) { is_expected.to eq %i(new create) }
132
+ end
133
+
134
+ context "except_to_update" do
135
+ let(:name) { :except_to_update }
136
+
137
+ its(:behavior) { is_expected.to eq :update }
138
+ its(:actions) { is_expected.to eq %i(index show new create destroy) }
139
+ its(:negated_actions) { is_expected.to eq %i(edit update) }
140
+ end
141
+
142
+ context "except_to_delete" do
143
+ let(:name) { :except_to_delete }
144
+
145
+ its(:behavior) { is_expected.to eq :delete }
146
+ its(:actions) { is_expected.to eq %i(index show new create edit update) }
147
+ its(:negated_actions) { is_expected.to eq %i(destroy) }
148
+ end
149
+ end
150
+ end
151
+ end