Class: Lockbox::Utils

Inherits:
Object
  • Object
show all
Defined in:
lib/lockbox/utils.rb

Class Method Summary collapse

Class Method Details

.build_box(context, options, table, attribute) ⇒ Object



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
# File 'lib/lockbox/utils.rb', line 3

def self.build_box(context, options, table, attribute)
  options = options.except(:attribute, :encrypted_attribute, :migrating, :attached, :type)
  options[:encode] = false unless options.key?(:encode)
  options.each do |k, v|
    if v.is_a?(Proc)
      options[k] = context.instance_exec(&v) if v.respond_to?(:call)
    elsif v.is_a?(Symbol)
      options[k] = context.send(v)
    end
  end

  unless options[:key] || options[:encryption_key] || options[:decryption_key]
    options[:key] = Lockbox.attribute_key(table: table, attribute: attribute, master_key: options.delete(:master_key))
  end

  if options[:previous_versions].is_a?(Array)
    options[:previous_versions] = options[:previous_versions].dup
    options[:previous_versions].each_with_index do |version, i|
      if !(version[:key] || version[:encryption_key] || version[:decryption_key]) && version[:master_key]
        options[:previous_versions][i] = version.merge(key: Lockbox.attribute_key(table: table, attribute: attribute, master_key: version.delete(:master_key)))
      end
    end
  end

  Lockbox.new(**options)
end

.decode_key(key, size: 32) ⇒ Object

Raises:



34
35
36
37
38
39
40
41
42
43
# File 'lib/lockbox/utils.rb', line 34

def self.decode_key(key, size: 32)
  if key.encoding != Encoding::BINARY && key =~ /\A[0-9a-f]{#{size * 2}}\z/i
    key = [key].pack("H*")
  end

  raise Lockbox::Error, "Key must use binary encoding" if key.encoding != Encoding::BINARY
  raise Lockbox::Error, "Key must be 32 bytes" if key.bytesize != size

  key
end

.decrypt_result(record, name, options, result) ⇒ Object



83
84
85
86
87
# File 'lib/lockbox/utils.rb', line 83

def self.decrypt_result(record, name, options, result)
  ActiveSupport::Notifications.instrument("decrypt_file.lockbox", {name: name}) do
    Utils.build_box(record, options, record.class.table_name, name).decrypt(result)
  end
end

.encrypt_attachable(record, name, attachable) ⇒ Object



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
# File 'lib/lockbox/utils.rb', line 49

def self.encrypt_attachable(record, name, attachable)
  io = nil

  ActiveSupport::Notifications.instrument("encrypt_file.lockbox", {name: name}) do
    options = encrypted_options(record, name)
    box = build_box(record, options, record.class.table_name, name)

    case attachable
    when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
      io = attachable
      attachable = {
        io: box.encrypt_io(io),
        filename: attachable.original_filename,
        content_type: attachable.content_type
      }
    when Hash
      io = attachable[:io]
      attachable = {
        io: box.encrypt_io(io),
        filename: attachable[:filename],
        content_type: attachable[:content_type]
      }
    else
      raise NotImplementedError, "Not supported"
    end
  end

  # set content type based on unencrypted data
  # keep synced with ActiveStorage::Blob#extract_content_type
  attachable[:io].extracted_content_type = Marcel::MimeType.for(io, name: attachable[:filename].to_s, declared_type: attachable[:content_type])

  attachable
end

.encrypted?(record, name) ⇒ Boolean

Returns:

  • (Boolean)


45
46
47
# File 'lib/lockbox/utils.rb', line 45

def self.encrypted?(record, name)
  !encrypted_options(record, name).nil?
end

.encrypted_options(record, name) ⇒ Object



30
31
32
# File 'lib/lockbox/utils.rb', line 30

def self.encrypted_options(record, name)
  record.class.respond_to?(:lockbox_attachments) ? record.class.lockbox_attachments[name.to_sym] : nil
end