sequel-privacy 0.5.5 → 0.5.7

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: 7dd8660d0cc93e21c96080ba66f777e9b39f00ec7a5ca4f8fa211739a97dbe62
4
- data.tar.gz: 85952e038e33a535adf8adaed566c08310e003f2540a406457029d678e889009
3
+ metadata.gz: f44402a685dc5c1569213ac155c7fba6ecbd291eb8a2091491f510105ddcc4d7
4
+ data.tar.gz: bc3a007c36fb539cd5f911b65c8bccf087218bd871b53991cfb9987cf44acc1c
5
5
  SHA512:
6
- metadata.gz: 9276223bd19d31daaabbaa34076e15c6dce4ce2439daadc30bee5e021fc7a88571323ce213ab2840afbe533b53d73129625817177a44b7a178f1fc93b6f21c37
7
- data.tar.gz: d6c2e72d8eea11c83b42be27a64dd5076e0e320a7d15efe7a6092ca543714203cf643ad14eee190b986eda570a331fca2b8d519126494ff35d5caef421f2fb9a
6
+ metadata.gz: c8ed3ffe946e6525075651c1965ded61114b44d0bfc10e67bcac71d5d2bfce7f8dcd682818fc6c1611eaca1ab8cfd5d709c24067d03c394edcb31b4de5b08593
7
+ data.tar.gz: 694cc27fb53f8ef0c68c7ab645c3f55251d1b3c838fdb9f52c9cf66bbbd33a8caff9d8969f0157f8fef8101562a99e570fbe3916d2d04389eccb7fc207a2aa5c
data/README.md CHANGED
@@ -180,6 +180,33 @@ policy :MyPolicy, ->() { ... },
180
180
 
181
181
  **`allow_anonymous: true`**: Skip the auto-deny for nil actor. Use for state-gate policies that examine only the subject (e.g. "post is published").
182
182
 
183
+ ### Policy Factories
184
+
185
+ Use `policy_factory` when a policy needs definition-time arguments, while still receiving the normal runtime policy arguments (`actor`, `subject`, `direct_object`) during enforcement.
186
+
187
+ ```ruby
188
+ module P
189
+ extend Sequel::Privacy::PolicyDSL
190
+
191
+ policy_factory :AllowIfActorMeetsFieldVisibility, ->(visibility_field) {
192
+ ->(actor, subject) {
193
+ allow if actor.meets_visibility?(subject.public_send(visibility_field))
194
+ }
195
+ }
196
+ end
197
+
198
+ class Member < Sequel::Model
199
+ plugin :privacy
200
+
201
+ privacy do
202
+ can :view, P::AllowMembers
203
+ field :phone, P::AllowIfActorMeetsFieldVisibility(:phone_visibility)
204
+ end
205
+ end
206
+ ```
207
+
208
+ Policy factories accept the same options as `policy`. The factory must return a Proc, and each call returns a concrete `Policy` instance with its own cache identity.
209
+
183
210
  ### Policy Combinators
184
211
 
185
212
  Use `all()` to require multiple conditions:
@@ -126,6 +126,8 @@ module Sequel
126
126
  case p
127
127
  when Sequel::Privacy::Policy, Proc
128
128
  p
129
+ when Sequel::Privacy::PolicyFactory
130
+ Kernel.raise ArgumentError, "Policy factory #{p.factory_name} must be called with arguments"
129
131
  else
130
132
  Kernel.raise ArgumentError, "Invalid policy: #{p.inspect}"
131
133
  end
@@ -61,7 +61,7 @@ module Sequel
61
61
 
62
62
  # Fail-secure: every chain ends with AlwaysDeny.
63
63
  unless policies.last == BuiltInPolicies::AlwaysDeny
64
- logger&.warn { 'Policy chain should end with AlwaysDeny. Appending it.' }
64
+ logger&.debug { 'Policy chain should end with AlwaysDeny. Appending it.' }
65
65
  policies = policies.dup << BuiltInPolicies::AlwaysDeny
66
66
  end
67
67
 
@@ -50,6 +50,35 @@ module Sequel
50
50
  )
51
51
  const_set(name, p)
52
52
  end
53
+
54
+ sig do
55
+ params(
56
+ name: Symbol,
57
+ factory: Proc,
58
+ comment: T.nilable(String),
59
+ cacheable: T::Boolean,
60
+ single_match: T::Boolean,
61
+ cache_by: T.nilable(T.any(Symbol, T::Array[Symbol])),
62
+ allow_anonymous: T::Boolean
63
+ ).void
64
+ end
65
+ def policy_factory(name, factory, comment = nil, cacheable: true, single_match: false, cache_by: nil,
66
+ allow_anonymous: false)
67
+ policy_factory = PolicyFactory.new(
68
+ name,
69
+ factory,
70
+ comment: comment,
71
+ cacheable: cacheable,
72
+ single_match: single_match,
73
+ cache_by: cache_by,
74
+ allow_anonymous: allow_anonymous
75
+ )
76
+
77
+ const_set(name, policy_factory)
78
+ define_singleton_method(name) do |*args|
79
+ policy_factory.call(*args)
80
+ end
81
+ end
53
82
  end
54
83
  end
55
84
  end
@@ -0,0 +1,68 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Sequel
5
+ module Privacy
6
+ # A PolicyFactory captures definition-time arguments and returns concrete
7
+ # Policy instances that can be registered in a privacy policy chain.
8
+ class PolicyFactory
9
+ extend T::Sig
10
+
11
+ sig { returns(String) }
12
+ attr_reader :factory_name
13
+
14
+ sig do
15
+ params(
16
+ factory_name: Symbol,
17
+ factory: Proc,
18
+ comment: T.nilable(String),
19
+ cacheable: T::Boolean,
20
+ single_match: T::Boolean,
21
+ cache_by: T.nilable(T.any(Symbol, T::Array[Symbol])),
22
+ allow_anonymous: T::Boolean
23
+ ).void
24
+ end
25
+ def initialize(factory_name, factory, comment: nil, cacheable: true, single_match: false, cache_by: nil,
26
+ allow_anonymous: false)
27
+ @factory_name = T.let(factory_name.to_s, String)
28
+ @factory = T.let(factory, Proc)
29
+ @comment = T.let(comment, T.nilable(String))
30
+ @cacheable = T.let(cacheable, T::Boolean)
31
+ @single_match = T.let(single_match, T::Boolean)
32
+ @cache_by = T.let(cache_by, T.nilable(T.any(Symbol, T::Array[Symbol])))
33
+ @allow_anonymous = T.let(allow_anonymous, T::Boolean)
34
+ end
35
+
36
+ sig { params(args: T.untyped).returns(Policy) }
37
+ def call(*args)
38
+ lam = T.unsafe(@factory).call(*args)
39
+ unless lam.is_a?(Proc)
40
+ Kernel.raise ArgumentError,
41
+ "Policy factory #{@factory_name} must return a Proc, got #{lam.inspect}"
42
+ end
43
+
44
+ T.cast(
45
+ Policy.create(
46
+ policy_name_for(args),
47
+ lam,
48
+ @comment,
49
+ cacheable: @cacheable,
50
+ single_match: @single_match,
51
+ cache_by: @cache_by,
52
+ allow_anonymous: @allow_anonymous
53
+ ),
54
+ Policy
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ sig { params(args: T::Array[T.untyped]).returns(Symbol) }
61
+ def policy_name_for(args)
62
+ return @factory_name.to_sym if args.empty?
63
+
64
+ :"#{@factory_name}(#{args.map(&:inspect).join(', ')})"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- VERSION = '0.5.5'
6
+ VERSION = '0.5.7'
7
7
  end
8
8
  end
@@ -20,6 +20,7 @@ require_relative 'sequel/privacy/version'
20
20
  require_relative 'sequel/privacy/errors'
21
21
  require_relative 'sequel/privacy/i_actor'
22
22
  require_relative 'sequel/privacy/policy'
23
+ require_relative 'sequel/privacy/policy_factory'
23
24
  require_relative 'sequel/privacy/cache'
24
25
  require_relative 'sequel/privacy/actions'
25
26
  require_relative 'sequel/privacy/viewer_context'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequel-privacy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Austin Bales
@@ -114,6 +114,7 @@ files:
114
114
  - lib/sequel/privacy/i_actor.rb
115
115
  - lib/sequel/privacy/policy.rb
116
116
  - lib/sequel/privacy/policy_dsl.rb
117
+ - lib/sequel/privacy/policy_factory.rb
117
118
  - lib/sequel/privacy/version.rb
118
119
  - lib/sequel/privacy/viewer_context.rb
119
120
  homepage: https://github.com/arbales/sequel-privacy