hubbado-policy 1.3.0 → 1.4.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/CHANGELOG.md +7 -0
- data/README.md +16 -9
- data/hubbado-policy.gemspec +1 -1
- data/lib/hubbado/policy/rspec_matchers/deny.rb +76 -0
- data/lib/hubbado/policy/rspec_matchers/permit.rb +49 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c13f91fb3fc199763adb101abd81452f10540df9977625ae846a6935ba5c8b4f
|
|
4
|
+
data.tar.gz: 30374987e2de06084366a8a90cdeee83412385fad96b33d2e8b40d99f6da2cf5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7f7d1193d9b680df272a3cb477a21fb7c3b441fda943b3a291e6620d5811178af47cb4b2c64a21e0ad67e6280fd28ae0d53bde8a9d3caa15d404e2fdf01d7de
|
|
7
|
+
data.tar.gz: 2e60851e3c9f8a3f32640475a2335b99503446863ebd2618d73a2e9fae0794f0e2570e2010014fa38387e068fb20b26af8eaf34458f96260fb1a0e6322e50721
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2025-10-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- rspec matcher `:permit` for policies
|
|
13
|
+
- rspec matcher `:deny` for policies
|
|
14
|
+
|
|
8
15
|
## [1.3.0] - 2025-06-03
|
|
9
16
|
|
|
10
17
|
### Added
|
data/README.md
CHANGED
|
@@ -40,17 +40,17 @@ Policy objects encapsulate authorization logic and determine whether certain act
|
|
|
40
40
|
class ArticlePolicy < Hubbado::Policy::Base
|
|
41
41
|
define_policy :view do
|
|
42
42
|
return permitted if user.admin?
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
if record.published?
|
|
45
|
-
permitted
|
|
45
|
+
permitted
|
|
46
46
|
else
|
|
47
47
|
denied(:not_published)
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
|
-
|
|
50
|
+
|
|
51
51
|
define_policy :edit do
|
|
52
52
|
return permitted if user.admin?
|
|
53
|
-
|
|
53
|
+
|
|
54
54
|
if record.author == user
|
|
55
55
|
permitted
|
|
56
56
|
else
|
|
@@ -137,7 +137,7 @@ You can also specify a custom i18n scope when returning denied results:
|
|
|
137
137
|
define_policy :edit do
|
|
138
138
|
# Use a different i18n scope for this specific denial
|
|
139
139
|
return denied(:not_authorized, i18n_scope: "custom_errors.article")
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
permitted
|
|
142
142
|
end
|
|
143
143
|
```
|
|
@@ -148,7 +148,7 @@ Both the class method and instance method versions of `denied` support the `i18n
|
|
|
148
148
|
# Class method
|
|
149
149
|
ArticlePolicy.denied(:custom_reason, i18n_scope: "errors.custom")
|
|
150
150
|
|
|
151
|
-
# Instance method
|
|
151
|
+
# Instance method
|
|
152
152
|
policy.denied(:custom_reason, i18n_scope: "errors.custom")
|
|
153
153
|
```
|
|
154
154
|
|
|
@@ -193,6 +193,13 @@ mimic_policy.deny(:view, data: { reason: "custom data" })
|
|
|
193
193
|
mimic_policy.deny(:view, :not_authorized, data: { user_id: 123 })
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
+
In addition, there are two RSpec matcher `permit` and `deny` and
|
|
197
|
+
have to be required manually:
|
|
198
|
+
```ruby
|
|
199
|
+
require 'hubbado/policy/rspec_matchers/permit'
|
|
200
|
+
require 'hubbado/policy/rspec_matchers/deny'
|
|
201
|
+
```
|
|
202
|
+
|
|
196
203
|
## Result Objects
|
|
197
204
|
|
|
198
205
|
Result objects represent the outcome of a policy check, containing:
|
|
@@ -271,11 +278,11 @@ class ArticleScope < Hubbado::Policy::Scope
|
|
|
271
278
|
def self.default_scope
|
|
272
279
|
Article.all
|
|
273
280
|
end
|
|
274
|
-
|
|
281
|
+
|
|
275
282
|
# Required: Implement the filtering logic
|
|
276
283
|
def resolve(record, scope, **options)
|
|
277
284
|
return scope if record.admin?
|
|
278
|
-
|
|
285
|
+
|
|
279
286
|
scope.where(published: true).or(scope.where(author_id: record.id))
|
|
280
287
|
end
|
|
281
288
|
end
|
|
@@ -299,7 +306,7 @@ You can pass custom base scopes:
|
|
|
299
306
|
```ruby
|
|
300
307
|
# Scope only to a specific category
|
|
301
308
|
category_articles = ArticleScope.call(
|
|
302
|
-
current_user,
|
|
309
|
+
current_user,
|
|
303
310
|
Article.where(category_id: params[:category_id])
|
|
304
311
|
)
|
|
305
312
|
|
data/hubbado-policy.gemspec
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Policy
|
|
3
|
+
module RspecMatchers
|
|
4
|
+
module Deny
|
|
5
|
+
def deny(...)
|
|
6
|
+
DenyMatcher.new(...)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class DenyMatcher
|
|
10
|
+
def initialize(action, *args, **kwargs)
|
|
11
|
+
@action = action
|
|
12
|
+
@args = args
|
|
13
|
+
@kwargs = kwargs
|
|
14
|
+
@reason = :denied
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def with(reason)
|
|
18
|
+
@reason = reason
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def matches?(policy)
|
|
23
|
+
@policy = policy
|
|
24
|
+
@result = policy.send @action, *@args, **@kwargs
|
|
25
|
+
|
|
26
|
+
unless @result.denied?
|
|
27
|
+
@incorrect_repsonse = true
|
|
28
|
+
return false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if @reason && @result.reason != @reason
|
|
32
|
+
@incorrect_reason = true
|
|
33
|
+
return false
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
begin
|
|
37
|
+
@result.message
|
|
38
|
+
rescue I18n::MissingTranslationData
|
|
39
|
+
@missing_translation = true
|
|
40
|
+
return false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def description
|
|
47
|
+
"#{@policy.class.name} deny #{@action} with #{@reason}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def failure_message
|
|
51
|
+
if @incorrect_reason
|
|
52
|
+
"Expected #{@policy.class.name} to deny #{@action}, with #{@reason} " \
|
|
53
|
+
"but it was denied with #{@result.reason}"
|
|
54
|
+
elsif @missing_translation
|
|
55
|
+
"Translation missing for policy #{@policy.class.name} and #{@reason}"
|
|
56
|
+
else
|
|
57
|
+
"Expected #{@policy.class.name} to deny #{@action}, but it was permitted"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def failure_message_when_negated
|
|
62
|
+
if @missing_translation
|
|
63
|
+
"Translation missing for policy #{@policy.class.name} and #{@reason}"
|
|
64
|
+
else
|
|
65
|
+
"Expected #{@policy.class.name} to permit #{@action}, but it was denied"
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
RSpec.configure do |rspec|
|
|
71
|
+
rspec.include self, type: :policy
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Hubbado
|
|
2
|
+
module Policy
|
|
3
|
+
module RspecMatchers
|
|
4
|
+
module Permit
|
|
5
|
+
def permit(...)
|
|
6
|
+
PermitMatcher.new(...)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
class PermitMatcher
|
|
10
|
+
def initialize(action, *args, **kwargs)
|
|
11
|
+
@action = action
|
|
12
|
+
@args = args
|
|
13
|
+
@kwargs = kwargs
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def matches?(policy)
|
|
17
|
+
@policy = policy
|
|
18
|
+
|
|
19
|
+
if policy.respond_to?(@action)
|
|
20
|
+
@result = policy.send @action, *@args, **@kwargs
|
|
21
|
+
|
|
22
|
+
@result.permitted?
|
|
23
|
+
else
|
|
24
|
+
# TODO: Get rid of this branch once the Navigation policy uses the DSL
|
|
25
|
+
policy.send "#{@action}?", *@args, **@kwargs
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def description
|
|
30
|
+
"#{@policy.class.name} permit #{@action}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def failure_message
|
|
34
|
+
"Expected #{@policy.class.name} to permit #{@action}, " \
|
|
35
|
+
"but it was revoked with #{@reason ? @result.reason : 'no reason'}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def failure_message_when_negated
|
|
39
|
+
"Expected #{@policy.class.name} to deny #{@action}, but it was permitted"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
RSpec.configure do |rspec|
|
|
44
|
+
rspec.include self, type: :policy
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: hubbado-policy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Hubbado Devs
|
|
@@ -124,6 +124,8 @@ files:
|
|
|
124
124
|
- lib/hubbado/policy/controls/policy.rb
|
|
125
125
|
- lib/hubbado/policy/railtie.rb
|
|
126
126
|
- lib/hubbado/policy/result.rb
|
|
127
|
+
- lib/hubbado/policy/rspec_matchers/deny.rb
|
|
128
|
+
- lib/hubbado/policy/rspec_matchers/permit.rb
|
|
127
129
|
- lib/hubbado/policy/scope.rb
|
|
128
130
|
homepage: https://github.com/hubbado/hubbado-policy
|
|
129
131
|
licenses:
|
|
@@ -146,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
146
148
|
- !ruby/object:Gem::Version
|
|
147
149
|
version: '0'
|
|
148
150
|
requirements: []
|
|
149
|
-
rubygems_version: 3.6.
|
|
151
|
+
rubygems_version: 3.6.9
|
|
150
152
|
specification_version: 4
|
|
151
153
|
summary: A lightweight, flexible policy framework for Ruby applications
|
|
152
154
|
test_files: []
|