rspec-authorization 0.0.2 → 0.0.6

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