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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 85715105fb8d91ba36116e0fe32fc87806a48960898d9e5b9802af20bba64e67
4
- data.tar.gz: 532065ab325a09f66d6f2dc18e89db1b77f2c2032f45ee424f6dc196dde99c8f
3
+ metadata.gz: c13f91fb3fc199763adb101abd81452f10540df9977625ae846a6935ba5c8b4f
4
+ data.tar.gz: 30374987e2de06084366a8a90cdeee83412385fad96b33d2e8b40d99f6da2cf5
5
5
  SHA512:
6
- metadata.gz: ff8d518316ca3e959685b16f9b76afc893686ff02d237de6ccc9573e14b2ac2114bc20bb0a69af9bb5af3bb8105732123b3d486ae1da3ba24e6fbb0054adac1a
7
- data.tar.gz: 793021cd69698ace3607c10d68cab0811032f2c93e1d2b71c497e5405ebd2f4b6141eabb023b2b2bb34b4eae7155fff0caa1f62912a35dad3267445aab2d515e
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
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "hubbado-policy"
3
- s.version = "1.3.0"
3
+ s.version = "1.4.0"
4
4
  s.summary = "A lightweight, flexible policy framework for Ruby applications"
5
5
 
6
6
  s.authors = ["Hubbado Devs"]
@@ -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.3.0
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.7
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: []