sequel-privacy 0.6.1 → 0.7.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 +6 -5
- data/README.md +18 -14
- data/lib/sequel/plugins/privacy.rb +5 -6
- data/lib/sequel/privacy/enforcer.rb +2 -0
- data/lib/sequel/privacy/errors.rb +3 -0
- data/lib/sequel/privacy/version.rb +1 -1
- data/lib/sequel/privacy/viewer_context.rb +52 -2
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7ef079bb3498aa1006aa327a138bcb1735b78ae02a65b11d2ee4976d9871978
|
|
4
|
+
data.tar.gz: 95000199db0cbb08908c1a261152c641b71ceb7898875a9f732936662df9334e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ea2e6410976a3e325264faa698a2faaf323c1aafdfe9eb3444c8cc18f51c887155227a1fe68ec119408d9679b1c4a39cc70e69611db4e6ed94edf9b93857ef2
|
|
7
|
+
data.tar.gz: 0271f5b12a6d376e04b5feae0eb9ee604da1258871092578878571c877b86b9ef388a60441962b31b36588c9c3e4fc9642c9b71cd0f90a8890030903e2a1bb21
|
data/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
3
|
|
|
8
4
|
## [Unreleased]
|
|
9
5
|
|
|
10
|
-
## [0.
|
|
6
|
+
## [0.7.0] – 2026-08-26
|
|
7
|
+
- Removed `#for_vc` on `Sequel::Model` instances. Instead, call `#reset_viewer_context(vc, reason)`.
|
|
8
|
+
- Removed `#viewer_context=` from `Sequel::Model` instances.
|
|
9
|
+
- Added `#use(&block)` to Omniscient and AllPowerful viewer contexts. The block receives the viewer context and the reason it was created.
|
|
10
|
+
|
|
11
|
+
## [0.1.0]
|
|
11
12
|
|
|
12
13
|
### Added
|
|
13
14
|
- Initial release
|
data/README.md
CHANGED
|
@@ -260,31 +260,35 @@ admin_vc = Sequel::Privacy::ViewerContext.all_powerful(:admin_migration)
|
|
|
260
260
|
|
|
261
261
|
### Login, Sessions & `current_user` and `current_vc`
|
|
262
262
|
|
|
263
|
-
Unless you allow unsafe access to your User (or equivalent) model, you will need
|
|
264
|
-
a way to load it and create a ViewerContext for them. An Omniscient ViewerContext
|
|
265
|
-
is useful for this.
|
|
266
|
-
or materialized a user from the session.
|
|
263
|
+
Unless you allow unsafe access to your User (or equivalent) model, you will need
|
|
264
|
+
a way to load it and create a ViewerContext for them. An Omniscient ViewerContext
|
|
265
|
+
is useful for this.
|
|
267
266
|
|
|
268
267
|
```ruby
|
|
269
|
-
|
|
270
|
-
# or however you store them. Discard this viewer context when you're done with it.
|
|
271
|
-
def current_user
|
|
268
|
+
def current_user
|
|
272
269
|
return @current_user if @current_user
|
|
273
|
-
login_vc = Sequel::Privacy::ViewerContext.omniscient(:login)
|
|
274
|
-
user = User.for_vc(login_vc)[session_user_id]
|
|
275
|
-
return nil unless user
|
|
276
270
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
271
|
+
@current_user = Sequel::Privacy::ViewerContext.omniscient(:login).use do |vc, reason|
|
|
272
|
+
user = User.for_vc(vc)[session_user_id]
|
|
273
|
+
next nil unless user
|
|
274
|
+
|
|
275
|
+
# Give the row its own ActorVC, so its fields and associations respect
|
|
276
|
+
# privacy from here on — and so it can be written to. An OmniscientVC reads
|
|
277
|
+
# anything but refuses to mutate.
|
|
278
|
+
user.reset_viewer_context(Sequel::Privacy::ViewerContext.for_actor(user), reason)
|
|
279
|
+
end
|
|
280
280
|
end
|
|
281
281
|
|
|
282
282
|
def current_vc
|
|
283
283
|
current_user&.viewer_context || Sequel::Privacy::ViewerContext.anonymous()
|
|
284
284
|
end
|
|
285
|
-
|
|
286
285
|
```
|
|
287
286
|
|
|
287
|
+
*Warning:* Skipping the reset leaves you holding an object that cannot be
|
|
288
|
+
saved, but any association or field reads on it will be allowed. So you should always
|
|
289
|
+
discard the OmniVC / APVCs used for things like this and call `reset_viewer_context`
|
|
290
|
+
with a more appropriate one as soon as you can.
|
|
291
|
+
|
|
288
292
|
## Mutation Enforcement
|
|
289
293
|
|
|
290
294
|
When a viewer context is attached, mutations are automatically checked:
|
|
@@ -639,13 +639,12 @@ module Sequel
|
|
|
639
639
|
@viewer_context = T.let(@viewer_context, T.nilable(Sequel::Privacy::ViewerContext))
|
|
640
640
|
end
|
|
641
641
|
|
|
642
|
-
sig { params(vc: T.nilable(Sequel::Privacy::ViewerContext)).returns(T.nilable(Sequel::Privacy::ViewerContext)) }
|
|
643
|
-
def viewer_context=(vc)
|
|
644
|
-
@viewer_context = T.let(vc, T.nilable(Sequel::Privacy::ViewerContext))
|
|
645
|
-
end
|
|
646
642
|
|
|
647
|
-
sig { params(vc: Sequel::Privacy::ViewerContext).returns(T.self_type) }
|
|
648
|
-
def
|
|
643
|
+
sig { params(vc: Sequel::Privacy::ViewerContext, reason: Symbol).returns(T.self_type) }
|
|
644
|
+
def reset_viewer_context(vc, reason)
|
|
645
|
+
Sequel::Privacy.logger&.debug do
|
|
646
|
+
"Resetting viewer context on #{self.class}[#{pk}] to #{vc.class.name.to_s.split('::').last} (#{reason})"
|
|
647
|
+
end
|
|
649
648
|
@viewer_context = T.let(vc, T.nilable(Sequel::Privacy::ViewerContext))
|
|
650
649
|
self
|
|
651
650
|
end
|
|
@@ -15,6 +15,9 @@ module Sequel
|
|
|
15
15
|
# Raised when an invalid viewer context is used
|
|
16
16
|
class InvalidViewerContextError < StandardError; end
|
|
17
17
|
|
|
18
|
+
# Raised if #invalidates was called on this VC.
|
|
19
|
+
class InvalidatedViewerContext < InvalidViewerContextError; end
|
|
20
|
+
|
|
18
21
|
class MissingViewerContext < StandardError; end
|
|
19
22
|
|
|
20
23
|
# Raised when attempting to modify privacy settings after finalization
|
|
@@ -41,6 +41,54 @@ module Sequel
|
|
|
41
41
|
def self.anonymous
|
|
42
42
|
AnonymousVC.new
|
|
43
43
|
end
|
|
44
|
+
|
|
45
|
+
sig { returns(T::Boolean) }
|
|
46
|
+
def invalidated?
|
|
47
|
+
@invalidated = T.let(@invalidated, T.nilable(T::Boolean))
|
|
48
|
+
@invalidated || false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
sig { void }
|
|
52
|
+
def assert_usable!
|
|
53
|
+
return unless invalidated?
|
|
54
|
+
|
|
55
|
+
Kernel.raise InvalidatedViewerContext,
|
|
56
|
+
"#{self.class.name.to_s.split('::').last} was invalidated and cannot be used again"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Made available on OmniscientVC and AllPowerfulVCs. Transient contexts are
|
|
61
|
+
# invalidated when the use block exists. To help you keep these around for
|
|
62
|
+
# as short a time as possible.
|
|
63
|
+
module TransientViewerContext
|
|
64
|
+
extend T::Sig
|
|
65
|
+
extend T::Helpers
|
|
66
|
+
|
|
67
|
+
abstract!
|
|
68
|
+
requires_ancestor { ViewerContext }
|
|
69
|
+
|
|
70
|
+
sig { abstract.returns(Symbol) }
|
|
71
|
+
def reason; end
|
|
72
|
+
|
|
73
|
+
sig do
|
|
74
|
+
type_parameters(:U)
|
|
75
|
+
.params(block: T.proc.params(vc: T.untyped, reason: Symbol).returns(T.type_parameter(:U)))
|
|
76
|
+
.returns(T.type_parameter(:U))
|
|
77
|
+
end
|
|
78
|
+
def use(&block)
|
|
79
|
+
block.call(self, reason)
|
|
80
|
+
ensure
|
|
81
|
+
invalidate!
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
sig { returns(T.self_type) }
|
|
85
|
+
def invalidate!
|
|
86
|
+
unless invalidated?
|
|
87
|
+
Sequel::Privacy.logger&.debug("Invalidating viewer context: #{reason}")
|
|
88
|
+
@invalidated = T.let(true, T.nilable(T::Boolean))
|
|
89
|
+
end
|
|
90
|
+
self
|
|
91
|
+
end
|
|
44
92
|
end
|
|
45
93
|
|
|
46
94
|
# Standard viewer context with an actor (user/member)
|
|
@@ -65,6 +113,7 @@ module Sequel
|
|
|
65
113
|
# Requires a reason for audit logging.
|
|
66
114
|
class AllPowerfulVC < ViewerContext
|
|
67
115
|
extend T::Sig
|
|
116
|
+
include TransientViewerContext
|
|
68
117
|
|
|
69
118
|
sig { params(reason: Symbol).void }
|
|
70
119
|
def initialize(reason)
|
|
@@ -72,7 +121,7 @@ module Sequel
|
|
|
72
121
|
super()
|
|
73
122
|
end
|
|
74
123
|
|
|
75
|
-
sig { returns(Symbol) }
|
|
124
|
+
sig { override.returns(Symbol) }
|
|
76
125
|
attr_reader :reason
|
|
77
126
|
end
|
|
78
127
|
|
|
@@ -80,6 +129,7 @@ module Sequel
|
|
|
80
129
|
# Used for system operations like authentication lookups.
|
|
81
130
|
class OmniscientVC < ViewerContext
|
|
82
131
|
extend T::Sig
|
|
132
|
+
include TransientViewerContext
|
|
83
133
|
|
|
84
134
|
sig { params(reason: Symbol).void }
|
|
85
135
|
def initialize(reason)
|
|
@@ -87,7 +137,7 @@ module Sequel
|
|
|
87
137
|
super()
|
|
88
138
|
end
|
|
89
139
|
|
|
90
|
-
sig { returns(Symbol) }
|
|
140
|
+
sig { override.returns(Symbol) }
|
|
91
141
|
attr_reader :reason
|
|
92
142
|
end
|
|
93
143
|
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Austin Bales
|
|
@@ -93,6 +93,20 @@ dependencies:
|
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '0.17'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: rubocop
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0'
|
|
103
|
+
type: :development
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - ">="
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0'
|
|
96
110
|
description: A Sequel plugin that provides declarative privacy policies and automatic
|
|
97
111
|
enforcement at field access and query boundaries.
|
|
98
112
|
email:
|
|
@@ -138,7 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
138
152
|
- !ruby/object:Gem::Version
|
|
139
153
|
version: '0'
|
|
140
154
|
requirements: []
|
|
141
|
-
rubygems_version: 4.0.
|
|
155
|
+
rubygems_version: 4.0.14
|
|
142
156
|
specification_version: 4
|
|
143
157
|
summary: Privacy enforcement plugin for Sequel models
|
|
144
158
|
test_files: []
|