File: scheme.rb

package info (click to toggle)
rails 2%3A7.2.2.1%2Bdfsg-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 43,352 kB
  • sloc: ruby: 349,799; javascript: 30,703; yacc: 46; sql: 43; sh: 29; makefile: 27
file content (100 lines) | stat: -rw-r--r-- 3,228 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# frozen_string_literal: true

module ActiveRecord
  module Encryption
    # A container of attribute encryption options.
    #
    # It validates and serves attribute encryption options.
    #
    # See EncryptedAttributeType, Context
    class Scheme
      attr_accessor :previous_schemes

      def initialize(key_provider: nil, key: nil, deterministic: nil, support_unencrypted_data: nil, downcase: nil, ignore_case: nil,
                     previous_schemes: nil, **context_properties)
        # Initializing all attributes to +nil+ as we want to allow a "not set" semantics so that we
        # can merge schemes without overriding values with defaults. See +#merge+

        @key_provider_param = key_provider
        @key = key
        @deterministic = deterministic
        @support_unencrypted_data = support_unencrypted_data
        @downcase = downcase || ignore_case
        @ignore_case = ignore_case
        @previous_schemes_param = previous_schemes
        @previous_schemes = Array.wrap(previous_schemes)
        @context_properties = context_properties

        validate_config!
      end

      def ignore_case?
        @ignore_case
      end

      def downcase?
        @downcase
      end

      def deterministic?
        !!@deterministic
      end

      def support_unencrypted_data?
        @support_unencrypted_data.nil? ? ActiveRecord::Encryption.config.support_unencrypted_data : @support_unencrypted_data
      end

      def fixed?
        # by default deterministic encryption is fixed
        @fixed ||= @deterministic && (!@deterministic.is_a?(Hash) || @deterministic[:fixed])
      end

      def key_provider
        @key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
      end

      def merge(other_scheme)
        self.class.new(**to_h.merge(other_scheme.to_h))
      end

      def to_h
        { key_provider: @key_provider_param, deterministic: @deterministic, downcase: @downcase, ignore_case: @ignore_case,
          previous_schemes: @previous_schemes_param, **@context_properties }.compact
      end

      def with_context(&block)
        if @context_properties.present?
          ActiveRecord::Encryption.with_encryption_context(**@context_properties, &block)
        else
          block.call
        end
      end

      def compatible_with?(other_scheme)
        deterministic? == other_scheme.deterministic?
      end

      private
        def validate_config!
          raise Errors::Configuration, "ignore_case: can only be used with deterministic encryption" if @ignore_case && !@deterministic
          raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
        end

        def key_provider_from_key
          @key_provider_from_key ||= if @key.present?
            DerivedSecretKeyProvider.new(@key)
          end
        end

        def deterministic_key_provider
          @deterministic_key_provider ||= if @deterministic
            DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
          end
        end

        def default_key_provider
          ActiveRecord::Encryption.key_provider
        end
    end
  end
end