sequel-privacy 0.5.7 → 0.6.1
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/lib/sequel/plugins/privacy.rb +81 -43
- data/lib/sequel/privacy/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 627da75fe2db0e41382bd4b6a6e44cf85bb01a3a52aef9c1c16df85b0b4af5d8
|
|
4
|
+
data.tar.gz: e0cfd6b79e35d81a51d6897fa37f9130e5fb567f3422c0014500be6bb0d95183
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c1450ee72abf6ef060a7d2f8d541f3d418724cca9650370746e996427d4da438e93fa96410b47b6606ab4a41542d014b46dbf97a08e3e4623cf120b419736372
|
|
7
|
+
data.tar.gz: 5ccadc4dfb1b84fb7ab9d3beefb2b396b2b1302239c0b63fc97a2c34a6fb9b5a025857542814167e007326a17e08e1697d56731cdff690a5c9d72d850cb96529
|
|
@@ -43,10 +43,15 @@ module Sequel
|
|
|
43
43
|
model.instance_variable_set(:@privacy_association_policies, {})
|
|
44
44
|
model.instance_variable_set(:@privacy_finalized, false)
|
|
45
45
|
model.instance_variable_set(:@allow_unsafe_access, false)
|
|
46
|
+
model.instance_variable_set(:@privacy_wrapped_association_reflections, {})
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
sig { params(model: T.class_of(Sequel::Model), opts: T::Hash[Symbol, T.untyped]).void }
|
|
49
|
-
def self.configure(model, opts = {})
|
|
50
|
+
def self.configure(model, opts = {})
|
|
51
|
+
model.all_association_reflections.each do |reflection|
|
|
52
|
+
model.send(:_setup_privacy_association_readers, reflection[:type], reflection[:name], true)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
50
55
|
|
|
51
56
|
class AssociationPrivacyDSL
|
|
52
57
|
extend T::Sig
|
|
@@ -146,6 +151,7 @@ module Sequel
|
|
|
146
151
|
:@privacy_policies => :dup,
|
|
147
152
|
:@privacy_fields => :dup,
|
|
148
153
|
:@privacy_association_policies => :dup,
|
|
154
|
+
:@privacy_wrapped_association_reflections => :dup,
|
|
149
155
|
:@privacy_finalized => nil,
|
|
150
156
|
:@allow_unsafe_access => nil
|
|
151
157
|
)
|
|
@@ -350,15 +356,7 @@ module Sequel
|
|
|
350
356
|
opts = _inject_privacy_eager_block(opts)
|
|
351
357
|
result = super
|
|
352
358
|
|
|
353
|
-
|
|
354
|
-
when :many_to_one, :one_to_one
|
|
355
|
-
_override_singular_association(name)
|
|
356
|
-
_override_association_dataset(name)
|
|
357
|
-
when :one_to_many, :many_to_many
|
|
358
|
-
_override_plural_association(name)
|
|
359
|
-
_override_association_dataset(name)
|
|
360
|
-
setup_association_privacy(name) if privacy_association_policies[name]
|
|
361
|
-
end
|
|
359
|
+
_setup_privacy_association_readers(type, name, false)
|
|
362
360
|
|
|
363
361
|
result
|
|
364
362
|
end
|
|
@@ -368,32 +366,74 @@ module Sequel
|
|
|
368
366
|
# DatasetMethods#post_load). Preserves any user-supplied block.
|
|
369
367
|
sig { params(opts: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
|
|
370
368
|
def _inject_privacy_eager_block(opts)
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
369
|
+
opts.merge(eager_block: _privacy_eager_block(opts[:eager_block]))
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
sig { params(original: T.untyped).returns(Proc) }
|
|
373
|
+
def _privacy_eager_block(original)
|
|
374
|
+
wrapped = proc do |dataset|
|
|
375
|
+
dataset = original.call(dataset) if original
|
|
374
376
|
vc = Thread.current[DatasetMethods::EAGER_VC_KEY]
|
|
375
|
-
if vc && T.unsafe(
|
|
376
|
-
T.unsafe(
|
|
377
|
+
if vc && T.unsafe(dataset).model.respond_to?(:privacy_vc_key)
|
|
378
|
+
T.unsafe(dataset).for_vc(vc).clone(privacy_eager_load: true)
|
|
377
379
|
else
|
|
378
|
-
|
|
380
|
+
dataset
|
|
379
381
|
end
|
|
380
382
|
end
|
|
381
|
-
|
|
383
|
+
wrapped
|
|
382
384
|
end
|
|
383
385
|
|
|
384
386
|
private
|
|
385
387
|
|
|
388
|
+
sig { returns(T::Module[T.anything]) }
|
|
389
|
+
def privacy_association_wrapper
|
|
390
|
+
wrapper = @privacy_association_wrapper ||= T.let(
|
|
391
|
+
Module.new,
|
|
392
|
+
T.nilable(T::Module[T.anything])
|
|
393
|
+
)
|
|
394
|
+
prepend(wrapper) unless ancestors.include?(wrapper)
|
|
395
|
+
wrapper
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
sig { params(type: Symbol, name: Symbol, inject_eager_block: T::Boolean).void }
|
|
399
|
+
def _setup_privacy_association_readers(type, name, inject_eager_block)
|
|
400
|
+
reflection = association_reflection(name)
|
|
401
|
+
return unless reflection
|
|
402
|
+
|
|
403
|
+
@privacy_wrapped_association_reflections ||= T.let(
|
|
404
|
+
{},
|
|
405
|
+
T.nilable(T::Hash[Symbol, T.untyped])
|
|
406
|
+
)
|
|
407
|
+
wrapped = @privacy_wrapped_association_reflections
|
|
408
|
+
return if wrapped[name].equal?(reflection)
|
|
409
|
+
|
|
410
|
+
if inject_eager_block
|
|
411
|
+
reflection[:eager_block] = _privacy_eager_block(reflection[:eager_block])
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
case type
|
|
415
|
+
when :many_to_one, :one_to_one
|
|
416
|
+
_override_singular_association(name)
|
|
417
|
+
_override_association_dataset(name)
|
|
418
|
+
when :one_to_many, :many_to_many
|
|
419
|
+
_override_plural_association(name)
|
|
420
|
+
_override_association_dataset(name)
|
|
421
|
+
setup_association_privacy(name) if privacy_association_policies[name]
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
wrapped[name] = reflection
|
|
425
|
+
end
|
|
426
|
+
|
|
386
427
|
sig { params(name: Symbol).void }
|
|
387
428
|
def _override_association_dataset(name)
|
|
388
429
|
dataset_method = :"#{name}_dataset"
|
|
389
430
|
return unless method_defined?(dataset_method)
|
|
390
431
|
|
|
391
|
-
original = instance_method(dataset_method)
|
|
392
432
|
assoc_reflection = association_reflection(name)
|
|
393
433
|
assoc_class = T.let(nil, T.nilable(T.class_of(Sequel::Model)))
|
|
394
434
|
|
|
395
|
-
define_method(dataset_method) do |*args|
|
|
396
|
-
ds =
|
|
435
|
+
privacy_association_wrapper.define_method(dataset_method) do |*args, &block|
|
|
436
|
+
ds = super(*args, &block)
|
|
397
437
|
vc = instance_variable_get(:@viewer_context)
|
|
398
438
|
return ds unless vc
|
|
399
439
|
|
|
@@ -408,12 +448,11 @@ module Sequel
|
|
|
408
448
|
|
|
409
449
|
sig { params(name: Symbol).void }
|
|
410
450
|
def _override_singular_association(name)
|
|
411
|
-
original = instance_method(name)
|
|
412
451
|
assoc_reflection = association_reflection(name)
|
|
413
452
|
# Resolve lazily to handle forward references between models.
|
|
414
453
|
assoc_class = T.let(nil, T.nilable(T.class_of(Sequel::Model)))
|
|
415
454
|
|
|
416
|
-
define_method(name) do
|
|
455
|
+
privacy_association_wrapper.define_method(name) do |*args, &block|
|
|
417
456
|
vc = instance_variable_get(:@viewer_context)
|
|
418
457
|
|
|
419
458
|
if vc.nil? && !T.unsafe(self.class).allow_unsafe_access?(name)
|
|
@@ -428,12 +467,12 @@ module Sequel
|
|
|
428
467
|
old_vc = Thread.current[vc_key]
|
|
429
468
|
Thread.current[vc_key] = vc
|
|
430
469
|
begin
|
|
431
|
-
|
|
470
|
+
super(*args, &block)
|
|
432
471
|
ensure
|
|
433
472
|
Thread.current[vc_key] = old_vc
|
|
434
473
|
end
|
|
435
474
|
else
|
|
436
|
-
|
|
475
|
+
super(*args, &block)
|
|
437
476
|
end
|
|
438
477
|
|
|
439
478
|
return nil unless obj
|
|
@@ -453,11 +492,10 @@ module Sequel
|
|
|
453
492
|
|
|
454
493
|
sig { params(name: Symbol).void }
|
|
455
494
|
def _override_plural_association(name)
|
|
456
|
-
original = instance_method(name)
|
|
457
495
|
assoc_reflection = association_reflection(name)
|
|
458
496
|
assoc_class = T.let(nil, T.nilable(T.class_of(Sequel::Model)))
|
|
459
497
|
|
|
460
|
-
define_method(name) do
|
|
498
|
+
privacy_association_wrapper.define_method(name) do |*args, &block|
|
|
461
499
|
vc = instance_variable_get(:@viewer_context)
|
|
462
500
|
|
|
463
501
|
if vc.nil? && !T.unsafe(self.class).allow_unsafe_access?(name)
|
|
@@ -472,12 +510,12 @@ module Sequel
|
|
|
472
510
|
old_vc = Thread.current[vc_key]
|
|
473
511
|
Thread.current[vc_key] = vc
|
|
474
512
|
begin
|
|
475
|
-
|
|
513
|
+
super(*args, &block)
|
|
476
514
|
ensure
|
|
477
515
|
Thread.current[vc_key] = old_vc
|
|
478
516
|
end
|
|
479
517
|
else
|
|
480
|
-
|
|
518
|
+
super(*args, &block)
|
|
481
519
|
end
|
|
482
520
|
|
|
483
521
|
return objs unless vc
|
|
@@ -499,13 +537,13 @@ module Sequel
|
|
|
499
537
|
sig { params(assoc_name: Symbol, singular_name: Symbol, policies: T::Array[T.untyped]).void }
|
|
500
538
|
def _wrap_association_add(assoc_name, singular_name, policies)
|
|
501
539
|
method_name = :"add_#{singular_name}"
|
|
502
|
-
original = instance_method(method_name)
|
|
503
540
|
|
|
504
|
-
define_method(method_name) do |
|
|
541
|
+
privacy_association_wrapper.define_method(method_name) do |*args, &block|
|
|
542
|
+
obj = args.first
|
|
505
543
|
vc = instance_variable_get(:@viewer_context)
|
|
506
544
|
|
|
507
545
|
unless vc
|
|
508
|
-
return
|
|
546
|
+
return super(*args, &block) if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
|
|
509
547
|
|
|
510
548
|
Kernel.raise Sequel::Privacy::MissingViewerContext,
|
|
511
549
|
"Cannot #{method_name} without a viewer context"
|
|
@@ -523,20 +561,20 @@ module Sequel
|
|
|
523
561
|
"Cannot #{method_name} on #{self.class}"
|
|
524
562
|
end
|
|
525
563
|
|
|
526
|
-
|
|
564
|
+
super(*args, &block)
|
|
527
565
|
end
|
|
528
566
|
end
|
|
529
567
|
|
|
530
568
|
sig { params(assoc_name: Symbol, singular_name: Symbol, policies: T::Array[T.untyped]).void }
|
|
531
569
|
def _wrap_association_remove(assoc_name, singular_name, policies)
|
|
532
570
|
method_name = :"remove_#{singular_name}"
|
|
533
|
-
original = instance_method(method_name)
|
|
534
571
|
|
|
535
|
-
define_method(method_name) do |
|
|
572
|
+
privacy_association_wrapper.define_method(method_name) do |*args, &block|
|
|
573
|
+
obj = args.first
|
|
536
574
|
vc = instance_variable_get(:@viewer_context)
|
|
537
575
|
|
|
538
576
|
unless vc
|
|
539
|
-
return
|
|
577
|
+
return super(*args, &block) if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
|
|
540
578
|
|
|
541
579
|
Kernel.raise Sequel::Privacy::MissingViewerContext,
|
|
542
580
|
"Cannot #{method_name} without a viewer context"
|
|
@@ -554,20 +592,19 @@ module Sequel
|
|
|
554
592
|
"Cannot #{method_name} on #{self.class}"
|
|
555
593
|
end
|
|
556
594
|
|
|
557
|
-
|
|
595
|
+
super(*args, &block)
|
|
558
596
|
end
|
|
559
597
|
end
|
|
560
598
|
|
|
561
599
|
sig { params(assoc_name: Symbol, plural_name: Symbol, policies: T::Array[T.untyped]).void }
|
|
562
600
|
def _wrap_association_remove_all(assoc_name, plural_name, policies)
|
|
563
601
|
method_name = :"remove_all_#{plural_name}"
|
|
564
|
-
original = instance_method(method_name)
|
|
565
602
|
|
|
566
|
-
define_method(method_name) do
|
|
603
|
+
privacy_association_wrapper.define_method(method_name) do |*args, &block|
|
|
567
604
|
vc = instance_variable_get(:@viewer_context)
|
|
568
605
|
|
|
569
606
|
unless vc
|
|
570
|
-
return
|
|
607
|
+
return super(*args, &block) if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
|
|
571
608
|
|
|
572
609
|
Kernel.raise Sequel::Privacy::MissingViewerContext,
|
|
573
610
|
"Cannot #{method_name} without a viewer context"
|
|
@@ -585,7 +622,7 @@ module Sequel
|
|
|
585
622
|
"Cannot #{method_name} on #{self.class}"
|
|
586
623
|
end
|
|
587
624
|
|
|
588
|
-
|
|
625
|
+
super(*args, &block)
|
|
589
626
|
end
|
|
590
627
|
end
|
|
591
628
|
end
|
|
@@ -716,8 +753,9 @@ module Sequel
|
|
|
716
753
|
# can retreive. Materializes the model, and then checks the view
|
|
717
754
|
# policy. If the model is being materialized within the context of
|
|
718
755
|
# checking a policy this is bypassed, because policies often need to
|
|
719
|
-
# check data that a VC might not have permission to see.
|
|
720
|
-
#
|
|
756
|
+
# check data that a VC might not have permission to see. For eager
|
|
757
|
+
# association loads, enforcement is deferred until association access so
|
|
758
|
+
# Sequel can finish attachment and populate reciprocal caches first.
|
|
721
759
|
sig { returns(T.untyped) }
|
|
722
760
|
def row_proc
|
|
723
761
|
vc = opts[:viewer_context]
|
|
@@ -737,8 +775,8 @@ module Sequel
|
|
|
737
775
|
next nil if instance.nil?
|
|
738
776
|
|
|
739
777
|
instance.instance_variable_set(:@viewer_context, vc)
|
|
778
|
+
next instance if opts[:privacy_eager_load]
|
|
740
779
|
next instance if Sequel::Privacy::Enforcer.in_policy_eval?
|
|
741
|
-
next instance if Thread.current[EAGER_VC_KEY]
|
|
742
780
|
|
|
743
781
|
if T.cast(instance, InstanceMethods).allow?(vc, :view)
|
|
744
782
|
instance
|
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.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Austin Bales
|
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
138
138
|
- !ruby/object:Gem::Version
|
|
139
139
|
version: '0'
|
|
140
140
|
requirements: []
|
|
141
|
-
rubygems_version:
|
|
141
|
+
rubygems_version: 4.0.15
|
|
142
142
|
specification_version: 4
|
|
143
143
|
summary: Privacy enforcement plugin for Sequel models
|
|
144
144
|
test_files: []
|