simple_authorize 0.1.0 → 1.0.0
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 +4 -4
- data/.simplecov +15 -0
- data/CHANGELOG.md +15 -0
- data/LICENSE.txt +1 -1
- data/README.md +210 -15
- data/SECURITY.md +73 -0
- data/lib/generators/simple_authorize/install/install_generator.rb +1 -0
- data/lib/generators/simple_authorize/install/templates/simple_authorize.rb +8 -0
- data/lib/generators/simple_authorize/policy/policy_generator.rb +55 -0
- data/lib/generators/simple_authorize/policy/templates/policy.rb.tt +39 -0
- data/lib/generators/simple_authorize/policy/templates/policy_spec.rb.tt +73 -0
- data/lib/generators/simple_authorize/policy/templates/policy_test.rb.tt +65 -0
- data/lib/simple_authorize/configuration.rb +21 -0
- data/lib/simple_authorize/controller.rb +336 -38
- data/lib/simple_authorize/policy.rb +22 -0
- data/lib/simple_authorize/railtie.rb +20 -0
- data/lib/simple_authorize/rspec.rb +149 -0
- data/lib/simple_authorize/test_helpers.rb +115 -0
- data/lib/simple_authorize/version.rb +1 -1
- data/lib/simple_authorize.rb +6 -17
- data/spec/examples.txt +51 -0
- data/spec/rspec_matchers_spec.rb +235 -0
- data/spec/spec_helper.rb +120 -0
- metadata +62 -4
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAuthorize
|
|
4
|
+
# RSpec matchers for SimpleAuthorize policies
|
|
5
|
+
#
|
|
6
|
+
# To use these matchers, add this to your spec/rails_helper.rb or spec/spec_helper.rb:
|
|
7
|
+
#
|
|
8
|
+
# require "simple_authorize/rspec"
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# RSpec.describe PostPolicy do
|
|
12
|
+
# subject { described_class.new(user, post) }
|
|
13
|
+
#
|
|
14
|
+
# context "as an admin" do
|
|
15
|
+
# let(:user) { build(:admin) }
|
|
16
|
+
#
|
|
17
|
+
# it { is_expected.to permit_action(:destroy) }
|
|
18
|
+
# it { is_expected.to forbid_action(:publish) }
|
|
19
|
+
# it { is_expected.to permit_viewing(:user_id) }
|
|
20
|
+
# it { is_expected.to permit_editing(:published) }
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
module RSpecMatchers
|
|
24
|
+
extend RSpec::Matchers::DSL
|
|
25
|
+
|
|
26
|
+
# Matcher for checking if a policy permits an action
|
|
27
|
+
#
|
|
28
|
+
# @example
|
|
29
|
+
# it { is_expected.to permit_action(:show) }
|
|
30
|
+
# expect(policy).to permit_action(:update)
|
|
31
|
+
matcher :permit_action do |action|
|
|
32
|
+
match do |policy|
|
|
33
|
+
action_method = action.to_s.end_with?("?") ? action.to_s : "#{action}?"
|
|
34
|
+
policy.public_send(action_method)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
failure_message do |policy|
|
|
38
|
+
"expected #{policy.class} to permit action :#{action} but it was forbidden"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
failure_message_when_negated do |policy|
|
|
42
|
+
"expected #{policy.class} to forbid action :#{action} but it was permitted"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Matcher for checking if a policy forbids an action
|
|
47
|
+
#
|
|
48
|
+
# @example
|
|
49
|
+
# it { is_expected.to forbid_action(:destroy) }
|
|
50
|
+
# expect(policy).to forbid_action(:update)
|
|
51
|
+
matcher :forbid_action do |action|
|
|
52
|
+
match do |policy|
|
|
53
|
+
action_method = action.to_s.end_with?("?") ? action.to_s : "#{action}?"
|
|
54
|
+
!policy.public_send(action_method)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
failure_message do |policy|
|
|
58
|
+
"expected #{policy.class} to forbid action :#{action} but it was permitted"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
failure_message_when_negated do |policy|
|
|
62
|
+
"expected #{policy.class} to permit action :#{action} but it was forbidden"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Matcher for checking if a policy permits viewing an attribute
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# it { is_expected.to permit_viewing(:title) }
|
|
70
|
+
# expect(policy).to permit_viewing(:email)
|
|
71
|
+
matcher :permit_viewing do |attribute|
|
|
72
|
+
match do |policy|
|
|
73
|
+
policy.attribute_visible?(attribute)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
failure_message do |policy|
|
|
77
|
+
"expected #{policy.class} to permit viewing :#{attribute} but it was hidden"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
failure_message_when_negated do |policy|
|
|
81
|
+
"expected #{policy.class} to forbid viewing :#{attribute} but it was visible"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Matcher for checking if a policy forbids viewing an attribute
|
|
86
|
+
#
|
|
87
|
+
# @example
|
|
88
|
+
# it { is_expected.to forbid_viewing(:password) }
|
|
89
|
+
# expect(policy).to forbid_viewing(:user_id)
|
|
90
|
+
matcher :forbid_viewing do |attribute|
|
|
91
|
+
match do |policy|
|
|
92
|
+
!policy.attribute_visible?(attribute)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
failure_message do |policy|
|
|
96
|
+
"expected #{policy.class} to forbid viewing :#{attribute} but it was visible"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
failure_message_when_negated do |policy|
|
|
100
|
+
"expected #{policy.class} to permit viewing :#{attribute} but it was hidden"
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Matcher for checking if a policy permits editing an attribute
|
|
105
|
+
#
|
|
106
|
+
# @example
|
|
107
|
+
# it { is_expected.to permit_editing(:title) }
|
|
108
|
+
# expect(policy).to permit_editing(:body)
|
|
109
|
+
matcher :permit_editing do |attribute|
|
|
110
|
+
match do |policy|
|
|
111
|
+
policy.attribute_editable?(attribute)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
failure_message do |policy|
|
|
115
|
+
"expected #{policy.class} to permit editing :#{attribute} but it was not editable"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
failure_message_when_negated do |policy|
|
|
119
|
+
"expected #{policy.class} to forbid editing :#{attribute} but it was editable"
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Matcher for checking if a policy forbids editing an attribute
|
|
124
|
+
#
|
|
125
|
+
# @example
|
|
126
|
+
# it { is_expected.to forbid_editing(:id) }
|
|
127
|
+
# expect(policy).to forbid_editing(:published)
|
|
128
|
+
matcher :forbid_editing do |attribute|
|
|
129
|
+
match do |policy|
|
|
130
|
+
!policy.attribute_editable?(attribute)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
failure_message do |policy|
|
|
134
|
+
"expected #{policy.class} to forbid editing :#{attribute} but it was editable"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
failure_message_when_negated do |policy|
|
|
138
|
+
"expected #{policy.class} to permit editing :#{attribute} but it was not editable"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Auto-include matchers if RSpec is loaded
|
|
145
|
+
if defined?(RSpec)
|
|
146
|
+
RSpec.configure do |config|
|
|
147
|
+
config.include SimpleAuthorize::RSpecMatchers
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module SimpleAuthorize
|
|
4
|
+
# Test helpers for Minitest
|
|
5
|
+
# Include this module in your test cases to get assertion methods for authorization testing
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class PostPolicyTest < ActiveSupport::TestCase
|
|
9
|
+
# include SimpleAuthorize::TestHelpers
|
|
10
|
+
#
|
|
11
|
+
# def test_admin_can_destroy
|
|
12
|
+
# policy = PostPolicy.new(admin_user, post)
|
|
13
|
+
# assert_permit_action(policy, :destroy)
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
module TestHelpers
|
|
17
|
+
# Assert that a policy permits a specific action
|
|
18
|
+
#
|
|
19
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
20
|
+
# @param action [Symbol, String] The action to check (e.g., :show, :update)
|
|
21
|
+
# @param message [String] Optional custom failure message
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# assert_permit_action(policy, :show)
|
|
25
|
+
# assert_permit_action(policy, "update")
|
|
26
|
+
def assert_permit_action(policy, action, message = nil)
|
|
27
|
+
action_method = action.to_s.end_with?("?") ? action.to_s : "#{action}?"
|
|
28
|
+
result = policy.public_send(action_method)
|
|
29
|
+
|
|
30
|
+
message ||= "Expected #{policy.class} to permit action :#{action} but it was forbidden"
|
|
31
|
+
assert result, message
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Assert that a policy forbids a specific action
|
|
35
|
+
#
|
|
36
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
37
|
+
# @param action [Symbol, String] The action to check (e.g., :destroy, :update)
|
|
38
|
+
# @param message [String] Optional custom failure message
|
|
39
|
+
#
|
|
40
|
+
# @example
|
|
41
|
+
# assert_forbid_action(policy, :destroy)
|
|
42
|
+
# assert_forbid_action(policy, "update")
|
|
43
|
+
def assert_forbid_action(policy, action, message = nil)
|
|
44
|
+
action_method = action.to_s.end_with?("?") ? action.to_s : "#{action}?"
|
|
45
|
+
result = policy.public_send(action_method)
|
|
46
|
+
|
|
47
|
+
message ||= "Expected #{policy.class} to forbid action :#{action} but it was permitted"
|
|
48
|
+
assert_not result, message
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Assert that a policy permits viewing a specific attribute
|
|
52
|
+
#
|
|
53
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
54
|
+
# @param attribute [Symbol, String] The attribute to check (e.g., :title, :email)
|
|
55
|
+
# @param message [String] Optional custom failure message
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
# assert_permit_viewing(policy, :title)
|
|
59
|
+
# assert_permit_viewing(policy, "email")
|
|
60
|
+
def assert_permit_viewing(policy, attribute, message = nil)
|
|
61
|
+
result = policy.attribute_visible?(attribute)
|
|
62
|
+
|
|
63
|
+
message ||= "Expected #{policy.class} to permit viewing :#{attribute} but it was hidden"
|
|
64
|
+
assert result, message
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Assert that a policy forbids viewing a specific attribute
|
|
68
|
+
#
|
|
69
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
70
|
+
# @param attribute [Symbol, String] The attribute to check (e.g., :password, :secret)
|
|
71
|
+
# @param message [String] Optional custom failure message
|
|
72
|
+
#
|
|
73
|
+
# @example
|
|
74
|
+
# assert_forbid_viewing(policy, :user_id)
|
|
75
|
+
# assert_forbid_viewing(policy, "password")
|
|
76
|
+
def assert_forbid_viewing(policy, attribute, message = nil)
|
|
77
|
+
result = policy.attribute_visible?(attribute)
|
|
78
|
+
|
|
79
|
+
message ||= "Expected #{policy.class} to forbid viewing :#{attribute} but it was visible"
|
|
80
|
+
assert_not result, message
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Assert that a policy permits editing a specific attribute
|
|
84
|
+
#
|
|
85
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
86
|
+
# @param attribute [Symbol, String] The attribute to check (e.g., :title, :body)
|
|
87
|
+
# @param message [String] Optional custom failure message
|
|
88
|
+
#
|
|
89
|
+
# @example
|
|
90
|
+
# assert_permit_editing(policy, :title)
|
|
91
|
+
# assert_permit_editing(policy, "body")
|
|
92
|
+
def assert_permit_editing(policy, attribute, message = nil)
|
|
93
|
+
result = policy.attribute_editable?(attribute)
|
|
94
|
+
|
|
95
|
+
message ||= "Expected #{policy.class} to permit editing :#{attribute} but it was not editable"
|
|
96
|
+
assert result, message
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Assert that a policy forbids editing a specific attribute
|
|
100
|
+
#
|
|
101
|
+
# @param policy [SimpleAuthorize::Policy] The policy instance to test
|
|
102
|
+
# @param attribute [Symbol, String] The attribute to check (e.g., :id, :created_at)
|
|
103
|
+
# @param message [String] Optional custom failure message
|
|
104
|
+
#
|
|
105
|
+
# @example
|
|
106
|
+
# assert_forbid_editing(policy, :published)
|
|
107
|
+
# assert_forbid_editing(policy, "id")
|
|
108
|
+
def assert_forbid_editing(policy, attribute, message = nil)
|
|
109
|
+
result = policy.attribute_editable?(attribute)
|
|
110
|
+
|
|
111
|
+
message ||= "Expected #{policy.class} to forbid editing :#{attribute} but it was editable"
|
|
112
|
+
assert_not result, message
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
data/lib/simple_authorize.rb
CHANGED
|
@@ -5,24 +5,13 @@ require_relative "simple_authorize/version"
|
|
|
5
5
|
require_relative "simple_authorize/configuration"
|
|
6
6
|
require_relative "simple_authorize/controller"
|
|
7
7
|
require_relative "simple_authorize/policy"
|
|
8
|
+
require_relative "simple_authorize/test_helpers"
|
|
8
9
|
|
|
10
|
+
# SimpleAuthorize provides a lightweight authorization framework for Rails applications
|
|
11
|
+
# without external dependencies. It offers policy-based access control inspired by Pundit.
|
|
9
12
|
module SimpleAuthorize
|
|
10
13
|
class Error < StandardError; end
|
|
11
|
-
|
|
12
|
-
# Railtie for automatic integration with Rails
|
|
13
|
-
class Railtie < Rails::Railtie
|
|
14
|
-
initializer "simple_authorize.configure" do
|
|
15
|
-
ActiveSupport.on_load(:action_controller) do
|
|
16
|
-
# Make SimpleAuthorize::Controller available as Authorization for backwards compatibility
|
|
17
|
-
::Authorization = SimpleAuthorize::Controller unless defined?(::Authorization)
|
|
18
|
-
# Make SimpleAuthorize::Policy available as ApplicationPolicy for backwards compatibility
|
|
19
|
-
::ApplicationPolicy = SimpleAuthorize::Policy unless defined?(::ApplicationPolicy)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Generate initializer template
|
|
24
|
-
generators do
|
|
25
|
-
require_relative "generators/simple_authorize/install/install_generator"
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
14
|
end
|
|
15
|
+
|
|
16
|
+
# Only load Railtie if Rails is defined
|
|
17
|
+
require_relative "simple_authorize/railtie" if defined?(Rails::Railtie)
|
data/spec/examples.txt
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
example_id | status | run_time |
|
|
2
|
+
-------------------------------------- | ------ | --------------- |
|
|
3
|
+
./spec/rspec_matchers_spec.rb[1:1:1:1] | passed | 0.00004 seconds |
|
|
4
|
+
./spec/rspec_matchers_spec.rb[1:1:1:2] | passed | 0.00004 seconds |
|
|
5
|
+
./spec/rspec_matchers_spec.rb[1:1:1:3] | passed | 0.00004 seconds |
|
|
6
|
+
./spec/rspec_matchers_spec.rb[1:1:2:1] | passed | 0.00004 seconds |
|
|
7
|
+
./spec/rspec_matchers_spec.rb[1:1:2:2] | passed | 0.00004 seconds |
|
|
8
|
+
./spec/rspec_matchers_spec.rb[1:1:3:1] | passed | 0.00005 seconds |
|
|
9
|
+
./spec/rspec_matchers_spec.rb[1:1:3:2] | passed | 0.00004 seconds |
|
|
10
|
+
./spec/rspec_matchers_spec.rb[1:1:4:1] | passed | 0.00004 seconds |
|
|
11
|
+
./spec/rspec_matchers_spec.rb[1:1:4:2] | passed | 0.00004 seconds |
|
|
12
|
+
./spec/rspec_matchers_spec.rb[1:2:1:1] | passed | 0.00004 seconds |
|
|
13
|
+
./spec/rspec_matchers_spec.rb[1:2:1:2] | passed | 0.00004 seconds |
|
|
14
|
+
./spec/rspec_matchers_spec.rb[1:2:2:1] | passed | 0.00005 seconds |
|
|
15
|
+
./spec/rspec_matchers_spec.rb[1:2:2:2] | passed | 0.00011 seconds |
|
|
16
|
+
./spec/rspec_matchers_spec.rb[1:2:3:1] | passed | 0.00005 seconds |
|
|
17
|
+
./spec/rspec_matchers_spec.rb[1:2:3:2] | passed | 0.00007 seconds |
|
|
18
|
+
./spec/rspec_matchers_spec.rb[1:2:4:1] | passed | 0.00003 seconds |
|
|
19
|
+
./spec/rspec_matchers_spec.rb[1:2:4:2] | passed | 0.00004 seconds |
|
|
20
|
+
./spec/rspec_matchers_spec.rb[1:3:1:1] | passed | 0.00004 seconds |
|
|
21
|
+
./spec/rspec_matchers_spec.rb[1:3:1:2] | passed | 0.00004 seconds |
|
|
22
|
+
./spec/rspec_matchers_spec.rb[1:3:1:3] | passed | 0.00004 seconds |
|
|
23
|
+
./spec/rspec_matchers_spec.rb[1:3:2:1] | passed | 0.00004 seconds |
|
|
24
|
+
./spec/rspec_matchers_spec.rb[1:3:3:1] | passed | 0.00005 seconds |
|
|
25
|
+
./spec/rspec_matchers_spec.rb[1:3:3:2] | passed | 0.00004 seconds |
|
|
26
|
+
./spec/rspec_matchers_spec.rb[1:3:4:1] | passed | 0.00003 seconds |
|
|
27
|
+
./spec/rspec_matchers_spec.rb[1:3:4:2] | passed | 0.00004 seconds |
|
|
28
|
+
./spec/rspec_matchers_spec.rb[1:4:1:1] | passed | 0.00004 seconds |
|
|
29
|
+
./spec/rspec_matchers_spec.rb[1:4:2:1] | passed | 0.00004 seconds |
|
|
30
|
+
./spec/rspec_matchers_spec.rb[1:4:2:2] | passed | 0.00004 seconds |
|
|
31
|
+
./spec/rspec_matchers_spec.rb[1:4:3:1] | passed | 0.00005 seconds |
|
|
32
|
+
./spec/rspec_matchers_spec.rb[1:4:4:1] | passed | 0.00003 seconds |
|
|
33
|
+
./spec/rspec_matchers_spec.rb[1:4:4:2] | passed | 0.00004 seconds |
|
|
34
|
+
./spec/rspec_matchers_spec.rb[1:5:1:1] | passed | 0.00004 seconds |
|
|
35
|
+
./spec/rspec_matchers_spec.rb[1:5:1:2] | passed | 0.00004 seconds |
|
|
36
|
+
./spec/rspec_matchers_spec.rb[1:5:2:1] | passed | 0.00004 seconds |
|
|
37
|
+
./spec/rspec_matchers_spec.rb[1:5:3:1] | passed | 0.00005 seconds |
|
|
38
|
+
./spec/rspec_matchers_spec.rb[1:5:3:2] | passed | 0.00004 seconds |
|
|
39
|
+
./spec/rspec_matchers_spec.rb[1:5:4:1] | passed | 0.00004 seconds |
|
|
40
|
+
./spec/rspec_matchers_spec.rb[1:5:4:2] | passed | 0.00004 seconds |
|
|
41
|
+
./spec/rspec_matchers_spec.rb[1:6:1:1] | passed | 0.00004 seconds |
|
|
42
|
+
./spec/rspec_matchers_spec.rb[1:6:2:1] | passed | 0.00004 seconds |
|
|
43
|
+
./spec/rspec_matchers_spec.rb[1:6:2:2] | passed | 0.00004 seconds |
|
|
44
|
+
./spec/rspec_matchers_spec.rb[1:6:3:1] | passed | 0.00004 seconds |
|
|
45
|
+
./spec/rspec_matchers_spec.rb[1:6:3:2] | passed | 0.00005 seconds |
|
|
46
|
+
./spec/rspec_matchers_spec.rb[1:6:4:1] | passed | 0.00003 seconds |
|
|
47
|
+
./spec/rspec_matchers_spec.rb[1:6:4:2] | passed | 0.00003 seconds |
|
|
48
|
+
./spec/rspec_matchers_spec.rb[1:7:1:1] | passed | 0.00004 seconds |
|
|
49
|
+
./spec/rspec_matchers_spec.rb[1:7:1:2] | passed | 0.00005 seconds |
|
|
50
|
+
./spec/rspec_matchers_spec.rb[1:7:1:3] | passed | 0.00003 seconds |
|
|
51
|
+
./spec/rspec_matchers_spec.rb[1:7:2:1] | passed | 0.00156 seconds |
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "spec_helper"
|
|
4
|
+
|
|
5
|
+
RSpec.describe SimpleAuthorize::RSpecMatchers do
|
|
6
|
+
let(:admin) { User.new(id: 1, role: :admin) }
|
|
7
|
+
let(:contributor) { User.new(id: 2, role: :contributor) }
|
|
8
|
+
let(:viewer) { User.new(id: 3, role: :viewer) }
|
|
9
|
+
let(:post) { Post.new(id: 1, user_id: 2) }
|
|
10
|
+
|
|
11
|
+
describe "permit_action matcher" do
|
|
12
|
+
context "when action is permitted" do
|
|
13
|
+
subject { PostPolicy.new(admin, post) }
|
|
14
|
+
|
|
15
|
+
it { is_expected.to permit_action(:destroy) }
|
|
16
|
+
it { is_expected.to permit_action(:update) }
|
|
17
|
+
it { is_expected.to permit_action(:show) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "when action is forbidden" do
|
|
21
|
+
subject { PostPolicy.new(viewer, post) }
|
|
22
|
+
|
|
23
|
+
it { is_expected.not_to permit_action(:destroy) }
|
|
24
|
+
it { is_expected.not_to permit_action(:update) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context "with string actions" do
|
|
28
|
+
subject { PostPolicy.new(admin, post) }
|
|
29
|
+
|
|
30
|
+
it { is_expected.to permit_action("show") }
|
|
31
|
+
it { is_expected.to permit_action("update") }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
context "with explicit expect syntax" do
|
|
35
|
+
it "passes when action is permitted" do
|
|
36
|
+
policy = PostPolicy.new(admin, post)
|
|
37
|
+
expect(policy).to permit_action(:destroy)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "fails when action is forbidden" do
|
|
41
|
+
policy = PostPolicy.new(viewer, post)
|
|
42
|
+
expect(policy).not_to permit_action(:destroy)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe "forbid_action matcher" do
|
|
48
|
+
context "when action is forbidden" do
|
|
49
|
+
subject { PostPolicy.new(viewer, post) }
|
|
50
|
+
|
|
51
|
+
it { is_expected.to forbid_action(:destroy) }
|
|
52
|
+
it { is_expected.to forbid_action(:update) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
context "when action is permitted" do
|
|
56
|
+
subject { PostPolicy.new(admin, post) }
|
|
57
|
+
|
|
58
|
+
it { is_expected.not_to forbid_action(:show) }
|
|
59
|
+
it { is_expected.not_to forbid_action(:index) }
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "with string actions" do
|
|
63
|
+
subject { PostPolicy.new(viewer, post) }
|
|
64
|
+
|
|
65
|
+
it { is_expected.to forbid_action("destroy") }
|
|
66
|
+
it { is_expected.to forbid_action("update") }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context "with explicit expect syntax" do
|
|
70
|
+
it "passes when action is forbidden" do
|
|
71
|
+
policy = PostPolicy.new(viewer, post)
|
|
72
|
+
expect(policy).to forbid_action(:destroy)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "fails when action is permitted" do
|
|
76
|
+
policy = PostPolicy.new(admin, post)
|
|
77
|
+
expect(policy).not_to forbid_action(:show)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe "permit_viewing matcher" do
|
|
83
|
+
context "when attribute is visible" do
|
|
84
|
+
subject { PostPolicy.new(viewer, post) }
|
|
85
|
+
|
|
86
|
+
it { is_expected.to permit_viewing(:title) }
|
|
87
|
+
it { is_expected.to permit_viewing(:body) }
|
|
88
|
+
it { is_expected.to permit_viewing(:id) }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
context "when attribute is hidden" do
|
|
92
|
+
subject { PostPolicy.new(viewer, post) }
|
|
93
|
+
|
|
94
|
+
it { is_expected.not_to permit_viewing(:user_id) }
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context "with string attributes" do
|
|
98
|
+
subject { PostPolicy.new(admin, post) }
|
|
99
|
+
|
|
100
|
+
it { is_expected.to permit_viewing("title") }
|
|
101
|
+
it { is_expected.to permit_viewing("user_id") }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
context "with explicit expect syntax" do
|
|
105
|
+
it "passes when attribute is visible" do
|
|
106
|
+
policy = PostPolicy.new(viewer, post)
|
|
107
|
+
expect(policy).to permit_viewing(:title)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "fails when attribute is hidden" do
|
|
111
|
+
policy = PostPolicy.new(viewer, post)
|
|
112
|
+
expect(policy).not_to permit_viewing(:user_id)
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "forbid_viewing matcher" do
|
|
118
|
+
context "when attribute is hidden" do
|
|
119
|
+
subject { PostPolicy.new(viewer, post) }
|
|
120
|
+
|
|
121
|
+
it { is_expected.to forbid_viewing(:user_id) }
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context "when attribute is visible" do
|
|
125
|
+
subject { PostPolicy.new(admin, post) }
|
|
126
|
+
|
|
127
|
+
it { is_expected.not_to forbid_viewing(:title) }
|
|
128
|
+
it { is_expected.not_to forbid_viewing(:user_id) }
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
context "with string attributes" do
|
|
132
|
+
subject { PostPolicy.new(viewer, post) }
|
|
133
|
+
|
|
134
|
+
it { is_expected.to forbid_viewing("user_id") }
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context "with explicit expect syntax" do
|
|
138
|
+
it "passes when attribute is hidden" do
|
|
139
|
+
policy = PostPolicy.new(viewer, post)
|
|
140
|
+
expect(policy).to forbid_viewing(:user_id)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it "fails when attribute is visible" do
|
|
144
|
+
policy = PostPolicy.new(admin, post)
|
|
145
|
+
expect(policy).not_to forbid_viewing(:title)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
describe "permit_editing matcher" do
|
|
151
|
+
context "when attribute is editable" do
|
|
152
|
+
subject { PostPolicy.new(contributor, post) }
|
|
153
|
+
|
|
154
|
+
it { is_expected.to permit_editing(:title) }
|
|
155
|
+
it { is_expected.to permit_editing(:body) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
context "when attribute is not editable" do
|
|
159
|
+
subject { PostPolicy.new(contributor, post) }
|
|
160
|
+
|
|
161
|
+
it { is_expected.not_to permit_editing(:published) }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "with string attributes" do
|
|
165
|
+
subject { PostPolicy.new(admin, post) }
|
|
166
|
+
|
|
167
|
+
it { is_expected.to permit_editing("title") }
|
|
168
|
+
it { is_expected.to permit_editing("published") }
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context "with explicit expect syntax" do
|
|
172
|
+
it "passes when attribute is editable" do
|
|
173
|
+
policy = PostPolicy.new(contributor, post)
|
|
174
|
+
expect(policy).to permit_editing(:title)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "fails when attribute is not editable" do
|
|
178
|
+
policy = PostPolicy.new(contributor, post)
|
|
179
|
+
expect(policy).not_to permit_editing(:published)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
describe "forbid_editing matcher" do
|
|
185
|
+
context "when attribute is not editable" do
|
|
186
|
+
subject { PostPolicy.new(contributor, post) }
|
|
187
|
+
|
|
188
|
+
it { is_expected.to forbid_editing(:published) }
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
context "when attribute is editable" do
|
|
192
|
+
subject { PostPolicy.new(admin, post) }
|
|
193
|
+
|
|
194
|
+
it { is_expected.not_to forbid_editing(:title) }
|
|
195
|
+
it { is_expected.not_to forbid_editing(:published) }
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context "with string attributes" do
|
|
199
|
+
subject { PostPolicy.new(viewer, post) }
|
|
200
|
+
|
|
201
|
+
it { is_expected.to forbid_editing("title") }
|
|
202
|
+
it { is_expected.to forbid_editing("published") }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
context "with explicit expect syntax" do
|
|
206
|
+
it "passes when attribute is not editable" do
|
|
207
|
+
policy = PostPolicy.new(contributor, post)
|
|
208
|
+
expect(policy).to forbid_editing(:published)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
it "fails when attribute is editable" do
|
|
212
|
+
policy = PostPolicy.new(admin, post)
|
|
213
|
+
expect(policy).not_to forbid_editing(:title)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
describe "edge cases" do
|
|
219
|
+
context "with nil user" do
|
|
220
|
+
subject { PostPolicy.new(nil, post) }
|
|
221
|
+
|
|
222
|
+
it { is_expected.to forbid_action(:update) }
|
|
223
|
+
it { is_expected.to forbid_viewing(:title) }
|
|
224
|
+
it { is_expected.to forbid_editing(:body) }
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
context "with missing policy methods" do
|
|
228
|
+
subject { PostPolicy.new(admin, post) }
|
|
229
|
+
|
|
230
|
+
it "raises NoMethodError for nonexistent actions" do
|
|
231
|
+
expect { subject.nonexistent_action? }.to raise_error(NoMethodError)
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|