symmetric-encryption 4.1.4 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b359cc79bc655a202fa3dca0882e311ec6211f204243e04a1e0775588ba7c433
4
- data.tar.gz: '08f689c03556c00b15a3810205ba1aece5577def975b15fd51a5ab8c70f31198'
3
+ metadata.gz: 9a62a8497a82ccb4701f2301bc351ab8e054569a3cdc02a0e0f2e14cc9866776
4
+ data.tar.gz: f8867d2e9ced1fd3f7adb081a851481cb3abc11f067d67104324bce539911e26
5
5
  SHA512:
6
- metadata.gz: 4e7ab43644e0bf6efdeaf07c2afd46bf3c58a17bf4396942bf4e28ad675c3892cee42bb04d4a801f7079c9456f65debc4ff13b36c6ec918a80f4cc3991b85022
7
- data.tar.gz: a8db0726395db261acb25cab050af5e3f82f5ad0a4fe7db263de4f7555e1c51f62911350aca6a5f59eb6b4eb46e791a4c98e4dda680c0364494605fbf07d89d6
6
+ metadata.gz: b8feb0c726ca19c1a956c24b1ec74c11786e07dc1d5131741697b23c0ff9a4b692a3b24853040b73e1418321a761d7f0687f51f620332c7480be73f5b23935fb
7
+ data.tar.gz: 26fb41fd4d2dc609eb21c382399ed1656798071eedcef94f127e4f4054f7c46f268b94c344ed73cf4ea51583dcbff08deb602df02c04b3dfd5e8fb6a3ea0b73d
@@ -1,32 +1,4 @@
1
- # Used for compression
2
- require 'zlib'
3
- # Used to coerce data types between string and their actual types
4
- require 'coercible'
5
-
6
- require 'symmetric_encryption/version'
7
- require 'symmetric_encryption/cipher'
8
- require 'symmetric_encryption/symmetric_encryption'
9
- require 'symmetric_encryption/exception'
10
-
11
- # @formatter:off
12
- module SymmetricEncryption
13
- autoload :Coerce, 'symmetric_encryption/coerce'
14
- autoload :Config, 'symmetric_encryption/config'
15
- autoload :Encoder, 'symmetric_encryption/encoder'
16
- autoload :Generator, 'symmetric_encryption/generator'
17
- autoload :Header, 'symmetric_encryption/header'
18
- autoload :Key, 'symmetric_encryption/key'
19
- autoload :Reader, 'symmetric_encryption/reader'
20
- autoload :RSAKey, 'symmetric_encryption/rsa_key'
21
- autoload :Writer, 'symmetric_encryption/writer'
22
- autoload :CLI, 'symmetric_encryption/cli'
23
- autoload :Keystore, 'symmetric_encryption/keystore'
24
- module Utils
25
- autoload :Aws, 'symmetric_encryption/utils/aws'
26
- autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
27
- end
28
- end
29
- # @formatter:on
1
+ require 'symmetric_encryption/core'
30
2
 
31
3
  # Add extensions. Gems are no longer order dependent.
32
4
  begin
@@ -36,26 +8,17 @@ rescue LoadError
36
8
  end
37
9
 
38
10
  begin
39
- require 'active_record'
40
- require 'symmetric_encryption/extensions/active_record/base'
41
- rescue LoadError
42
- end
43
-
44
- begin
45
- require 'active_model'
46
- require 'symmetric_encryption/railties/symmetric_encryption_validator'
47
- rescue LoadError
48
- end
11
+ require 'active_support'
12
+ ActiveSupport.on_load(:active_record) do
13
+ require 'symmetric_encryption/railties/attr_encrypted'
14
+ require 'symmetric_encryption/railties/symmetric_encryption_validator'
49
15
 
50
- begin
51
- require 'mongoid'
52
- require 'symmetric_encryption/extensions/mongoid/encrypted'
53
- rescue LoadError
54
- end
16
+ ActiveRecord::Base.include(SymmetricEncryption::Railties::AttrEncrypted)
17
+ end
55
18
 
56
- begin
57
- require 'mongo_mapper'
58
- warn 'MongoMapper support is deprecated. Please upgrade to Mongoid.'
59
- require 'symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key'
19
+ ActiveSupport.on_load(:mongoid) do
20
+ require 'symmetric_encryption/railties/mongoid_encrypted'
21
+ require 'symmetric_encryption/railties/symmetric_encryption_validator'
22
+ end
60
23
  rescue LoadError
61
24
  end
@@ -8,7 +8,7 @@ module SymmetricEncryption
8
8
  :environments, :cipher_name, :rolling_deploy, :rotate_keys, :rotate_kek, :prompt, :show_version,
9
9
  :cleanup_keys, :activate_key, :migrate, :regions
10
10
 
11
- KEYSTORES = %i[aws heroku environment file].freeze
11
+ KEYSTORES = %i[aws heroku environment file gcp].freeze
12
12
 
13
13
  def self.run!(argv)
14
14
  new(argv).run!
@@ -131,7 +131,7 @@ module SymmetricEncryption
131
131
  @generate = config
132
132
  end
133
133
 
134
- opts.on '-s', '--keystore heroku|environment|file|aws', 'Which keystore to use during generation or re-encryption.' do |keystore|
134
+ opts.on '-s', '--keystore heroku|environment|file|aws|gcp', 'Which keystore to use during generation or re-encryption.' do |keystore|
135
135
  @keystore = (keystore || 'file').downcase.to_sym
136
136
  end
137
137
 
@@ -0,0 +1,30 @@
1
+ # Used for compression
2
+ require 'zlib'
3
+ # Used to coerce data types between string and their actual types
4
+ require 'coercible'
5
+
6
+ require 'symmetric_encryption/version'
7
+ require 'symmetric_encryption/cipher'
8
+ require 'symmetric_encryption/symmetric_encryption'
9
+ require 'symmetric_encryption/exception'
10
+
11
+ # @formatter:off
12
+ module SymmetricEncryption
13
+ autoload :Coerce, 'symmetric_encryption/coerce'
14
+ autoload :Config, 'symmetric_encryption/config'
15
+ autoload :Encoder, 'symmetric_encryption/encoder'
16
+ autoload :Generator, 'symmetric_encryption/generator'
17
+ autoload :Header, 'symmetric_encryption/header'
18
+ autoload :Key, 'symmetric_encryption/key'
19
+ autoload :Reader, 'symmetric_encryption/reader'
20
+ autoload :RSAKey, 'symmetric_encryption/rsa_key'
21
+ autoload :Writer, 'symmetric_encryption/writer'
22
+ autoload :CLI, 'symmetric_encryption/cli'
23
+ autoload :Keystore, 'symmetric_encryption/keystore'
24
+ module Utils
25
+ autoload :Aws, 'symmetric_encryption/utils/aws'
26
+ autoload :Files, 'symmetric_encryption/utils/files'
27
+ autoload :ReEncryptFiles, 'symmetric_encryption/utils/re_encrypt_files'
28
+ end
29
+ end
30
+ # @formatter:on
@@ -4,6 +4,7 @@ module SymmetricEncryption
4
4
  # @formatter:off
5
5
  autoload :Aws, 'symmetric_encryption/keystore/aws'
6
6
  autoload :Environment, 'symmetric_encryption/keystore/environment'
7
+ autoload :Gcp, 'symmetric_encryption/keystore/gcp'
7
8
  autoload :File, 'symmetric_encryption/keystore/file'
8
9
  autoload :Heroku, 'symmetric_encryption/keystore/heroku'
9
10
  autoload :Memory, 'symmetric_encryption/keystore/memory'
@@ -1,4 +1,3 @@
1
- require 'base64'
2
1
  require 'aws-sdk-kms'
3
2
  module SymmetricEncryption
4
3
  module Keystore
@@ -51,6 +50,8 @@ module SymmetricEncryption
51
50
  # - Loss of access to AWS accounts.
52
51
  # - Loss of region(s) in which master keys are stored.
53
52
  class Aws
53
+ include Utils::Files
54
+
54
55
  attr_reader :region, :key_files, :master_key_alias
55
56
 
56
57
  # Returns [Hash] a new keystore configuration after generating the data key.
@@ -131,13 +132,8 @@ module SymmetricEncryption
131
132
  raise(SymmetricEncryption::ConfigError, "region: #{region} not available in the supplied key_files") unless key_file
132
133
 
133
134
  file_name = key_file[:file_name]
134
- raise(SymmetricEncryption::ConfigError, 'file_name is mandatory for each key_file entry') unless file_name
135
-
136
- raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
137
135
 
138
- # TODO: Validate that file is not globally readable.
139
- encoded_dek = ::File.open(file_name, 'rb', &:read)
140
- encrypted_data_key = Base64.strict_decode64(encoded_dek)
136
+ encrypted_data_key = read_file_and_decode(file_name)
141
137
  aws(region).decrypt(encrypted_data_key)
142
138
  end
143
139
 
@@ -150,24 +146,13 @@ module SymmetricEncryption
150
146
  raise(ArgumentError, 'region and file_name are mandatory for each key_file entry') unless region && file_name
151
147
 
152
148
  encrypted_data_key = aws(region).encrypt(data_key)
153
- encoded_dek = Base64.strict_encode64(encrypted_data_key)
154
- write_to_file(file_name, encoded_dek)
149
+ write_encoded_to_file(file_name, encrypted_data_key)
155
150
  end
156
151
  end
157
152
 
158
153
  def aws(region)
159
154
  Utils::Aws.new(region: region, master_key_alias: master_key_alias)
160
155
  end
161
-
162
- private
163
-
164
- # Write to the supplied file_name, backing up the existing file if present
165
- def write_to_file(file_name, data)
166
- path = ::File.dirname(file_name)
167
- ::FileUtils.mkdir_p(path) unless ::File.directory?(path)
168
- ::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
169
- ::File.open(file_name, 'wb') { |file| file.write(data) }
170
- end
171
156
  end
172
157
  end
173
158
  end
@@ -1,6 +1,8 @@
1
1
  module SymmetricEncryption
2
2
  module Keystore
3
3
  class File
4
+ include Utils::Files
5
+
4
6
  attr_accessor :file_name, :key_encrypting_key
5
7
 
6
8
  # Returns [Hash] a new keystore configuration after generating the data key.
@@ -51,33 +53,18 @@ module SymmetricEncryption
51
53
  "Symmetric Encryption key file '#{file_name}' has the wrong "\
52
54
  "permissions: #{::File.stat(file_name).mode.to_s(8)}. Expected 100600.") unless correct_permissions?
53
55
 
54
- data = read_from_file
56
+ data = read_from_file(file_name)
55
57
  key_encrypting_key ? key_encrypting_key.decrypt(data) : data
56
58
  end
57
59
 
58
60
  # Encrypt and write the key to file.
59
61
  def write(key)
60
62
  data = key_encrypting_key ? key_encrypting_key.encrypt(key) : key
61
- write_to_file(data)
63
+ write_to_file(file_name, data)
62
64
  end
63
65
 
64
66
  private
65
67
 
66
- # Read from the file, raising an exception if it is not found
67
- def read_from_file
68
- ::File.open(file_name, 'rb', &:read)
69
- rescue Errno::ENOENT
70
- raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
71
- end
72
-
73
- # Write to the supplied file_name, backing up the existing file if present
74
- def write_to_file(data)
75
- key_path = ::File.dirname(file_name)
76
- ::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
77
- ::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
78
- ::File.open(file_name, 'wb', 0600) { |file| file.write(data) }
79
- end
80
-
81
68
  # Returns true if the file is owned by the user running this code and it
82
69
  # has the correct mode - readable and writable by its owner and no one
83
70
  # else, much like the keys one has in ~/.ssh
@@ -0,0 +1,87 @@
1
+ require "google/cloud/kms/v1"
2
+
3
+ module SymmetricEncryption
4
+ module Keystore
5
+ class Gcp
6
+ include Utils::Files
7
+
8
+ def self.generate_data_key(version: 0, cipher_name:, app_name:, environment:, key_path:)
9
+ version >= 255 ? (version = 1) : (version += 1)
10
+
11
+ dek = SymmetricEncryption::Key.new(cipher_name: cipher_name)
12
+ file_name = "#{key_path}/#{app_name}_#{environment}_v#{version}.encrypted_key"
13
+ keystore = new(
14
+ key_file: file_name,
15
+ app_name: app_name,
16
+ environment: environment
17
+ )
18
+ keystore.write(dek.key)
19
+
20
+ {
21
+ keystore: :gcp,
22
+ cipher_name: dek.cipher_name,
23
+ version: version,
24
+ key_file: file_name,
25
+ iv: dek.iv,
26
+ crypto_key: keystore.crypto_key
27
+ }
28
+ end
29
+
30
+ def initialize(key_file:, app_name: nil, environment: nil, key_encrypting_key: nil, crypto_key: nil, project_id: nil, credentials: nil, location_id: nil)
31
+ @crypto_key = crypto_key
32
+ @app_name = app_name
33
+ @environment = environment
34
+ @file_name = key_file
35
+ @project_id = project_id
36
+ @credentials = credentials
37
+ @location_id = location_id
38
+ end
39
+
40
+ def read
41
+ decrypt(read_file_and_decode(file_name))
42
+ end
43
+
44
+ def write(data_key)
45
+ write_encoded_to_file(file_name, encrypt(data_key))
46
+ end
47
+
48
+ def crypto_key
49
+ @crypto_key ||= self.class::KMS::KeyManagementServiceClient.crypto_key_path(project_id, location_id, app_name, environment.to_s)
50
+ end
51
+
52
+ private
53
+
54
+ KMS = Google::Cloud::Kms::V1
55
+
56
+ attr_reader :app_name, :environment
57
+
58
+ def encrypt(plaintext)
59
+ client.encrypt(crypto_key, plaintext).ciphertext
60
+ end
61
+
62
+ def decrypt(ciphertext)
63
+ client.decrypt(crypto_key, ciphertext).plaintext
64
+ end
65
+
66
+ def client
67
+ self.class::KMS::KeyManagementServiceClient.new(timeout: 2, credentials: credentials)
68
+ end
69
+
70
+ def project_id
71
+ @project_id ||= ENV["GOOGLE_CLOUD_PROJECT"]
72
+ raise 'GOOGLE_CLOUD_PROJECT must be set' if @project_id.nil?
73
+ @project_id
74
+ end
75
+
76
+ def credentials
77
+ @credentials ||= ENV['GOOGLE_CLOUD_KEYFILE']
78
+ raise 'GOOGLE_CLOUD_KEYFILE must be set' if @credentials.nil?
79
+ @credentials
80
+ end
81
+
82
+ def location_id
83
+ @location_id ||= ENV["GOOGLE_CLOUD_LOCATION"] || 'global'
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,118 @@
1
+ module SymmetricEncryption
2
+ module Railties
3
+ module AttrEncrypted
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ end
7
+
8
+ module ClassMethods
9
+ # Transparently encrypt and decrypt values stored via ActiveRecord.
10
+ #
11
+ # Parameters:
12
+ # * Symbolic names of each method to create which has a corresponding
13
+ # method already defined in rails starting with: encrypted_
14
+ # * Followed by an optional hash:
15
+ # :random_iv [true|false]
16
+ # Whether the encrypted value should use a random IV every time the
17
+ # field is encrypted.
18
+ # It is recommended to set this to true where feasible. If the encrypted
19
+ # value could be used as part of a SQL where clause, or as part
20
+ # of any lookup, then it must be false.
21
+ # Setting random_iv to true will result in a different encrypted output for
22
+ # the same input string.
23
+ # Note: Only set to true if the field will never be used as part of
24
+ # the where clause in an SQL query.
25
+ # Note: When random_iv is true it will add a 8 byte header, plus the bytes
26
+ # to store the random IV in every returned encrypted string, prior to the
27
+ # encoding if any.
28
+ # Default: false
29
+ # Highly Recommended where feasible: true
30
+ #
31
+ # :type [Symbol]
32
+ # The type for this field, #see SymmetricEncryption::COERCION_TYPES
33
+ # Default: :string
34
+ #
35
+ # :compress [true|false]
36
+ # Whether to compress str before encryption
37
+ # Should only be used for large strings since compression overhead and
38
+ # the overhead of adding the 'magic' header may exceed any benefits of
39
+ # compression
40
+ # Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
41
+ # Default: false
42
+ def attr_encrypted(*params)
43
+ # Ensure ActiveRecord has created all its methods first
44
+ # Ignore failures since the table may not yet actually exist
45
+ begin
46
+ define_attribute_methods
47
+ rescue StandardError
48
+ nil
49
+ end
50
+
51
+ options = params.last.is_a?(Hash) ? params.pop.dup : {}
52
+
53
+ params.each do |attribute|
54
+ SymmetricEncryption::Generator.generate_decrypted_accessors(self, attribute, "encrypted_#{attribute}", options)
55
+ encrypted_attributes[attribute.to_sym] = "encrypted_#{attribute}".to_sym
56
+ end
57
+ end
58
+
59
+ # Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
60
+ # names as values
61
+ #
62
+ # Example
63
+ #
64
+ # class User < ActiveRecord::Base
65
+ # attr_encrypted :email
66
+ # end
67
+ #
68
+ # User.encrypted_attributes => { email: encrypted_email }
69
+ def encrypted_attributes
70
+ @encrypted_attributes ||= superclass.respond_to?(:encrypted_attributes) ? superclass.encrypted_attributes.dup : {}
71
+ end
72
+
73
+ # Return the name of all encrypted virtual attributes as an Array of symbols
74
+ # Example: [:email, :password]
75
+ def encrypted_keys
76
+ @encrypted_keys ||= encrypted_attributes.keys
77
+ end
78
+
79
+ # Return the name of all encrypted columns as an Array of symbols
80
+ # Example: [:encrypted_email, :encrypted_password]
81
+ def encrypted_columns
82
+ @encrypted_columns ||= encrypted_attributes.values
83
+ end
84
+
85
+ # Returns whether an attribute has been configured to be encrypted
86
+ #
87
+ # Example
88
+ #
89
+ # class User < ActiveRecord::Base
90
+ # attr_accessor :name
91
+ # attr_encrypted :email
92
+ # end
93
+ #
94
+ # User.encrypted_attribute?(:name) # false
95
+ # User.encrypted_attribute?(:email) # true
96
+ def encrypted_attribute?(attribute)
97
+ encrypted_keys.include?(attribute)
98
+ end
99
+
100
+ # Returns whether the attribute is the database column to hold the
101
+ # encrypted data for a matching encrypted attribute
102
+ #
103
+ # Example
104
+ #
105
+ # class User < ActiveRecord::Base
106
+ # attr_accessor :name
107
+ # attr_encrypted :email
108
+ # end
109
+ #
110
+ # User.encrypted_column?(:encrypted_name) # false
111
+ # User.encrypted_column?(:encrypted_email) # true
112
+ def encrypted_column?(attribute)
113
+ encrypted_columns.include?(attribute)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,45 @@
1
+ module SymmetricEncryption
2
+ module Utils
3
+ module Files
4
+ private
5
+
6
+ attr_reader :file_name
7
+
8
+ def read_file_and_decode(file_name)
9
+ raise(SymmetricEncryption::ConfigError, 'file_name is mandatory for each key_file entry') unless file_name
10
+
11
+ raise(SymmetricEncryption::ConfigError, "File #{file_name} could not be found") unless ::File.exist?(file_name)
12
+
13
+ # TODO: Validate that file is not globally readable.
14
+ decode64(read_from_file(file_name))
15
+ end
16
+
17
+ def write_encoded_to_file(file_name, encrypted_data_key)
18
+ write_to_file(file_name, encode64(encrypted_data_key))
19
+ end
20
+
21
+ def encode64(data)
22
+ Base64.strict_encode64(data)
23
+ end
24
+
25
+ def decode64(data)
26
+ Base64.strict_decode64(data)
27
+ end
28
+
29
+ # Write to the supplied file_name, backing up the existing file if present
30
+ def write_to_file(file_name, data)
31
+ key_path = ::File.dirname(file_name)
32
+ ::FileUtils.mkdir_p(key_path) unless ::File.directory?(key_path)
33
+ ::File.rename(file_name, "#{file_name}.#{Time.now.to_i}") if ::File.exist?(file_name)
34
+ ::File.open(file_name, 'wb', 0600) { |file| file.write(data) }
35
+ end
36
+
37
+ # Read from the file, raising an exception if it is not found
38
+ def read_from_file(file_name)
39
+ ::File.open(file_name, 'rb', &:read)
40
+ rescue Errno::ENOENT
41
+ raise(SymmetricEncryption::ConfigError, "Symmetric Encryption key file: '#{file_name}' not found or readable")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,3 +1,3 @@
1
1
  module SymmetricEncryption
2
- VERSION = '4.1.4'.freeze
2
+ VERSION = '4.2.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: symmetric-encryption
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.4
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reid Morrison
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-12 00:00:00.000000000 Z
11
+ date: 2019-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coercible
@@ -42,11 +42,9 @@ files:
42
42
  - lib/symmetric_encryption/cli.rb
43
43
  - lib/symmetric_encryption/coerce.rb
44
44
  - lib/symmetric_encryption/config.rb
45
+ - lib/symmetric_encryption/core.rb
45
46
  - lib/symmetric_encryption/encoder.rb
46
47
  - lib/symmetric_encryption/exception.rb
47
- - lib/symmetric_encryption/extensions/active_record/base.rb
48
- - lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb
49
- - lib/symmetric_encryption/extensions/mongoid/encrypted.rb
50
48
  - lib/symmetric_encryption/generator.rb
51
49
  - lib/symmetric_encryption/header.rb
52
50
  - lib/symmetric_encryption/key.rb
@@ -54,14 +52,18 @@ files:
54
52
  - lib/symmetric_encryption/keystore/aws.rb
55
53
  - lib/symmetric_encryption/keystore/environment.rb
56
54
  - lib/symmetric_encryption/keystore/file.rb
55
+ - lib/symmetric_encryption/keystore/gcp.rb
57
56
  - lib/symmetric_encryption/keystore/heroku.rb
58
57
  - lib/symmetric_encryption/keystore/memory.rb
59
58
  - lib/symmetric_encryption/railtie.rb
59
+ - lib/symmetric_encryption/railties/attr_encrypted.rb
60
+ - lib/symmetric_encryption/railties/mongoid_encrypted.rb
60
61
  - lib/symmetric_encryption/railties/symmetric_encryption_validator.rb
61
62
  - lib/symmetric_encryption/reader.rb
62
63
  - lib/symmetric_encryption/rsa_key.rb
63
64
  - lib/symmetric_encryption/symmetric_encryption.rb
64
65
  - lib/symmetric_encryption/utils/aws.rb
66
+ - lib/symmetric_encryption/utils/files.rb
65
67
  - lib/symmetric_encryption/utils/re_encrypt_files.rb
66
68
  - lib/symmetric_encryption/version.rb
67
69
  - lib/symmetric_encryption/writer.rb
@@ -1,110 +0,0 @@
1
- module ActiveRecord #:nodoc:
2
- class Base
3
- # Transparently encrypt and decrypt values stored via ActiveRecord.
4
- #
5
- # Parameters:
6
- # * Symbolic names of each method to create which has a corresponding
7
- # method already defined in rails starting with: encrypted_
8
- # * Followed by an optional hash:
9
- # :random_iv [true|false]
10
- # Whether the encrypted value should use a random IV every time the
11
- # field is encrypted.
12
- # It is recommended to set this to true where feasible. If the encrypted
13
- # value could be used as part of a SQL where clause, or as part
14
- # of any lookup, then it must be false.
15
- # Setting random_iv to true will result in a different encrypted output for
16
- # the same input string.
17
- # Note: Only set to true if the field will never be used as part of
18
- # the where clause in an SQL query.
19
- # Note: When random_iv is true it will add a 8 byte header, plus the bytes
20
- # to store the random IV in every returned encrypted string, prior to the
21
- # encoding if any.
22
- # Default: false
23
- # Highly Recommended where feasible: true
24
- #
25
- # :type [Symbol]
26
- # The type for this field, #see SymmetricEncryption::COERCION_TYPES
27
- # Default: :string
28
- #
29
- # :compress [true|false]
30
- # Whether to compress str before encryption
31
- # Should only be used for large strings since compression overhead and
32
- # the overhead of adding the 'magic' header may exceed any benefits of
33
- # compression
34
- # Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
35
- # Default: false
36
- def self.attr_encrypted(*params)
37
- # Ensure ActiveRecord has created all its methods first
38
- # Ignore failures since the table may not yet actually exist
39
- begin
40
- define_attribute_methods
41
- rescue StandardError
42
- nil
43
- end
44
-
45
- options = params.last.is_a?(Hash) ? params.pop.dup : {}
46
-
47
- params.each do |attribute|
48
- SymmetricEncryption::Generator.generate_decrypted_accessors(self, attribute, "encrypted_#{attribute}", options)
49
- encrypted_attributes[attribute.to_sym] = "encrypted_#{attribute}".to_sym
50
- end
51
- end
52
-
53
- # Contains a hash of encrypted attributes with virtual attribute names as keys and real attribute
54
- # names as values
55
- #
56
- # Example
57
- #
58
- # class User < ActiveRecord::Base
59
- # attr_encrypted :email
60
- # end
61
- #
62
- # User.encrypted_attributes => { email: encrypted_email }
63
- def self.encrypted_attributes
64
- @encrypted_attributes ||= superclass.respond_to?(:encrypted_attributes) ? superclass.encrypted_attributes.dup : {}
65
- end
66
-
67
- # Return the name of all encrypted virtual attributes as an Array of symbols
68
- # Example: [:email, :password]
69
- def self.encrypted_keys
70
- @encrypted_keys ||= encrypted_attributes.keys
71
- end
72
-
73
- # Return the name of all encrypted columns as an Array of symbols
74
- # Example: [:encrypted_email, :encrypted_password]
75
- def self.encrypted_columns
76
- @encrypted_columns ||= encrypted_attributes.values
77
- end
78
-
79
- # Returns whether an attribute has been configured to be encrypted
80
- #
81
- # Example
82
- #
83
- # class User < ActiveRecord::Base
84
- # attr_accessor :name
85
- # attr_encrypted :email
86
- # end
87
- #
88
- # User.encrypted_attribute?(:name) # false
89
- # User.encrypted_attribute?(:email) # true
90
- def self.encrypted_attribute?(attribute)
91
- encrypted_keys.include?(attribute)
92
- end
93
-
94
- # Returns whether the attribute is the database column to hold the
95
- # encrypted data for a matching encrypted attribute
96
- #
97
- # Example
98
- #
99
- # class User < ActiveRecord::Base
100
- # attr_accessor :name
101
- # attr_encrypted :email
102
- # end
103
- #
104
- # User.encrypted_column?(:encrypted_name) # false
105
- # User.encrypted_column?(:encrypted_email) # true
106
- def self.encrypted_column?(attribute)
107
- encrypted_columns.include?(attribute)
108
- end
109
- end
110
- end
@@ -1,41 +0,0 @@
1
- #
2
- # DEPRECATED !!!
3
- #
4
- module MongoMapper
5
- module Plugins
6
- module EncryptedKey
7
- extend ActiveSupport::Concern
8
-
9
- COERCION_MAP = {
10
- String => :string,
11
- Integer => :integer,
12
- Float => :float,
13
- BigDecimal => :decimal,
14
- DateTime => :datetime,
15
- Time => :time,
16
- Date => :date,
17
- Boolean => :boolean,
18
- Hash => :json
19
- }.freeze
20
-
21
- module ClassMethods
22
- def encrypted_key(key_name, type, full_options = {})
23
- full_options = full_options.is_a?(Hash) ? full_options.dup : {}
24
- options = full_options.delete(:encrypted) || {}
25
- # Support overriding the name of the decrypted attribute
26
- encrypted_key_name = options.delete(:encrypt_as) || "encrypted_#{key_name}"
27
- options[:type] = COERCION_MAP[type] unless %i[yaml json].include?(options[:type])
28
-
29
- raise(ArgumentError, "Invalid type: #{type.inspect}. Valid types: #{COERCION_MAP.keys.join(',')}") unless options[:type]
30
-
31
- SymmetricEncryption::Generator.generate_decrypted_accessors(self, key_name, encrypted_key_name, options)
32
-
33
- key(encrypted_key_name, String, full_options)
34
- end
35
- end
36
- end
37
- end
38
- end
39
-
40
- MongoMapper::Document.plugin(MongoMapper::Plugins::EncryptedKey)
41
- MongoMapper::EmbeddedDocument.plugin(MongoMapper::Plugins::EncryptedKey)