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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f44402a685dc5c1569213ac155c7fba6ecbd291eb8a2091491f510105ddcc4d7
4
- data.tar.gz: bc3a007c36fb539cd5f911b65c8bccf087218bd871b53991cfb9987cf44acc1c
3
+ metadata.gz: 627da75fe2db0e41382bd4b6a6e44cf85bb01a3a52aef9c1c16df85b0b4af5d8
4
+ data.tar.gz: e0cfd6b79e35d81a51d6897fa37f9130e5fb567f3422c0014500be6bb0d95183
5
5
  SHA512:
6
- metadata.gz: c8ed3ffe946e6525075651c1965ded61114b44d0bfc10e67bcac71d5d2bfce7f8dcd682818fc6c1611eaca1ab8cfd5d709c24067d03c394edcb31b4de5b08593
7
- data.tar.gz: 694cc27fb53f8ef0c68c7ab645c3f55251d1b3c838fdb9f52c9cf66bbbd33a8caff9d8969f0157f8fef8101562a99e570fbe3916d2d04389eccb7fc207a2aa5c
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 = {}); end
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
- case type
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
- original = opts[:eager_block]
372
- wrapped = proc do |ds|
373
- ds = original.call(ds) if original
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(ds).model.respond_to?(:privacy_vc_key)
376
- T.unsafe(ds).for_vc(vc)
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
- ds
380
+ dataset
379
381
  end
380
382
  end
381
- opts.merge(eager_block: wrapped)
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 = original.bind(self).(*args)
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
- original.bind(self).()
470
+ super(*args, &block)
432
471
  ensure
433
472
  Thread.current[vc_key] = old_vc
434
473
  end
435
474
  else
436
- original.bind(self).()
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
- original.bind(self).()
513
+ super(*args, &block)
476
514
  ensure
477
515
  Thread.current[vc_key] = old_vc
478
516
  end
479
517
  else
480
- original.bind(self).()
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 |obj|
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 original.bind(self).(obj) if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
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
- original.bind(self).(obj)
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 |obj|
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 original.bind(self).(obj) if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
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
- original.bind(self).(obj)
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 original.bind(self).() if T.unsafe(self.class).allow_unsafe_access?(assoc_name)
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
- original.bind(self).()
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. The check is also
720
- # bypassed for eager loads, and checked on the association.
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
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Sequel
5
5
  module Privacy
6
- VERSION = '0.5.7'
6
+ VERSION = '0.6.1'
7
7
  end
8
8
  end
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.7
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: 3.6.9
141
+ rubygems_version: 4.0.15
142
142
  specification_version: 4
143
143
  summary: Privacy enforcement plugin for Sequel models
144
144
  test_files: []