portunus 0.3.0
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 +7 -0
- data/.gitignore +3 -0
- data/.nvmrc +1 -0
- data/.travis.yml +27 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +189 -0
- data/LICENSE.txt +21 -0
- data/README.md +289 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/Rakefile +4 -0
- data/lib/generators/install_generator.rb +26 -0
- data/lib/generators/templates/create_portunus.rb.erb +28 -0
- data/lib/portunus/configuration.rb +49 -0
- data/lib/portunus/data_encryption_key.rb +25 -0
- data/lib/portunus/data_key_generator.rb +55 -0
- data/lib/portunus/encryptable.rb +42 -0
- data/lib/portunus/encrypters/open_ssl_aes.rb +58 -0
- data/lib/portunus/encrypters/time.rb +1 -0
- data/lib/portunus/field_configurer.rb +157 -0
- data/lib/portunus/hasher.rb +9 -0
- data/lib/portunus/master_key.rb +15 -0
- data/lib/portunus/railtie.rb +14 -0
- data/lib/portunus/rotators/dek.rb +69 -0
- data/lib/portunus/rotators/kek.rb +51 -0
- data/lib/portunus/storage_adaptors/credentials.rb +48 -0
- data/lib/portunus/storage_adaptors/environment.rb +67 -0
- data/lib/portunus/tasks/generate_keys.rake +36 -0
- data/lib/portunus/tasks/rotate_keys.rake +36 -0
- data/lib/portunus/type_caster.rb +37 -0
- data/lib/portunus/type_casters/boolean.rb +39 -0
- data/lib/portunus/type_casters/date.rb +29 -0
- data/lib/portunus/type_casters/date_time.rb +29 -0
- data/lib/portunus/type_casters/float.rb +29 -0
- data/lib/portunus/type_casters/integer.rb +29 -0
- data/lib/portunus/type_casters/string.rb +29 -0
- data/lib/portunus/version.rb +3 -0
- data/lib/portunus.rb +39 -0
- data/portunus.gemspec +51 -0
- data/tmp/log/development.log +0 -0
- metadata +255 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
class CreatePortunus < ActiveRecord::Migration<%= migration_version %>
|
2
|
+
def change
|
3
|
+
create_table :portunus_data_encryption_keys do |t|
|
4
|
+
t.string :encrypted_key, null: false
|
5
|
+
t.string :master_keyname, null: false
|
6
|
+
t.string :encryptable_type, null: false
|
7
|
+
t.integer :encryptable_id, null: false
|
8
|
+
t.datetime :last_dek_rotation
|
9
|
+
t.datetime :last_kek_rotation
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index(
|
14
|
+
:portunus_data_encryption_keys,
|
15
|
+
[:encryptable_id, :encryptable_type],
|
16
|
+
unique: true,
|
17
|
+
name: "portunus_dek_on_encryptable_id_and_encryptable_type"
|
18
|
+
)
|
19
|
+
add_index(
|
20
|
+
:portunus_data_encryption_keys,
|
21
|
+
:last_dek_rotation
|
22
|
+
)
|
23
|
+
add_index(
|
24
|
+
:portunus_data_encryption_keys,
|
25
|
+
:last_kek_rotation
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Portunus
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :storage_adaptor, :encrypter, :max_key_duration
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@storage_adaptor = ::Portunus::StorageAdaptors::Credentials
|
7
|
+
@encrypter = ::Portunus::Encrypters::OpenSslAes
|
8
|
+
@keys_loaded = false
|
9
|
+
@master_key_names = []
|
10
|
+
@max_key_duration = 6.months
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_keys
|
14
|
+
storage_adaptor.load
|
15
|
+
@keys_loaded = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_key(key_name)
|
19
|
+
@master_key_names.push(key_name)
|
20
|
+
# we want to load all the names of the keys in the storage
|
21
|
+
# adaptor. Because we might need to search through an environment
|
22
|
+
# that has quite a few keys often for every key we want to load
|
23
|
+
# the valid keys
|
24
|
+
end
|
25
|
+
|
26
|
+
def keys_loaded?
|
27
|
+
@keys_loaded
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset_master_keys
|
31
|
+
# Clear all master keys to empty, used for testing
|
32
|
+
@master_key_names = []
|
33
|
+
@keys_loaded = false
|
34
|
+
end
|
35
|
+
|
36
|
+
def reload_master_keys
|
37
|
+
# Perform a reload on the master keys. This is used in tests and to
|
38
|
+
# add new keys into the environment without rebooting the app.
|
39
|
+
@master_key_names = []
|
40
|
+
load_keys
|
41
|
+
end
|
42
|
+
|
43
|
+
def master_key_names
|
44
|
+
load_keys unless keys_loaded?
|
45
|
+
|
46
|
+
@master_key_names
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Portunus
|
2
|
+
class DataEncryptionKey < ::ActiveRecord::Base
|
3
|
+
belongs_to :encryptable, polymorphic: true
|
4
|
+
|
5
|
+
def key
|
6
|
+
::Portunus.configuration.encrypter.decrypt(
|
7
|
+
key: master_encryption_key.value,
|
8
|
+
value: encrypted_key
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def master_keyname=(new_key_value)
|
13
|
+
@_master_encryption_key = nil
|
14
|
+
super(new_key_value)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def master_encryption_key
|
20
|
+
@_master_encryption_key ||= Portunus.configuration.storage_adaptor.lookup(
|
21
|
+
master_keyname.to_sym
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Portunus
|
2
|
+
class DataKeyGenerator
|
3
|
+
def self.generate(encrypted_object)
|
4
|
+
new(encrypted_object: encrypted_object).generate
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(
|
8
|
+
encrypted_object:,
|
9
|
+
encrypter: ::Portunus.configuration.encrypter,
|
10
|
+
key_finder: Portunus.configuration.storage_adaptor
|
11
|
+
)
|
12
|
+
@encrypter = encrypter
|
13
|
+
@key_finder = key_finder
|
14
|
+
@encrypted_object = encrypted_object
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
dek = encrypted_object.build_data_encryption_key(
|
19
|
+
encrypted_key: new_encrypted_key,
|
20
|
+
master_keyname: master_keyname
|
21
|
+
)
|
22
|
+
|
23
|
+
if dek.key != new_plaintext_key
|
24
|
+
raise ::Portunus::Error.new(
|
25
|
+
"Dek Key creation failed: Decrypted key does not match the original"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
dek
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :encrypted_object, :encrypter, :key_finder
|
35
|
+
|
36
|
+
def new_encrypted_key
|
37
|
+
@_new_encrypted_key ||= encrypter.encrypt(
|
38
|
+
key: master_encryption_key.value, value: new_plaintext_key
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def new_plaintext_key
|
43
|
+
# this will be a base64 encoded key
|
44
|
+
@_new_key ||= encrypter.generate_key
|
45
|
+
end
|
46
|
+
|
47
|
+
def master_keyname
|
48
|
+
@_master_keyname ||= key_finder.list.sample
|
49
|
+
end
|
50
|
+
|
51
|
+
def master_encryption_key
|
52
|
+
@_master_encryption_key = key_finder.lookup(master_keyname)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Portunus
|
2
|
+
module Encryptable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
class_methods do
|
6
|
+
def encrypted_fields_list
|
7
|
+
@_encrypted_fields_list ||= []
|
8
|
+
end
|
9
|
+
|
10
|
+
def encrypted_fields(*fields)
|
11
|
+
fields.map do |field|
|
12
|
+
::Portunus::FieldConfigurer.for(self, field)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
included do
|
18
|
+
before_validation :hash_encrypted_fields
|
19
|
+
|
20
|
+
has_one(
|
21
|
+
:data_encryption_key,
|
22
|
+
as: :encryptable,
|
23
|
+
class_name: "::Portunus::DataEncryptionKey"
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def hash_encrypted_fields
|
30
|
+
self.class.encrypted_fields_list.each do |field|
|
31
|
+
hashed_field_name = "hashed_#{field}".to_sym
|
32
|
+
|
33
|
+
if respond_to?(hashed_field_name)
|
34
|
+
write_attribute(
|
35
|
+
hashed_field_name,
|
36
|
+
::Portunus::Hasher.for(send(field.to_sym))
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Portunus
|
2
|
+
module Encrypters
|
3
|
+
class OpenSslAes
|
4
|
+
def self.encrypt(key:, value:)
|
5
|
+
new(key: key, value: value).encrypt
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.generate_key
|
9
|
+
cipher = OpenSSL::Cipher::AES256.new :CBC
|
10
|
+
cipher.encrypt
|
11
|
+
Base64.strict_encode64(cipher.random_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.decrypt(key:, value:)
|
15
|
+
new(key: key, value: value).decrypt
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(key:, value:)
|
19
|
+
@key = Base64.strict_decode64(key)
|
20
|
+
@value = value
|
21
|
+
end
|
22
|
+
|
23
|
+
def encrypt
|
24
|
+
cipher = OpenSSL::Cipher::AES256.new :CBC
|
25
|
+
cipher.encrypt
|
26
|
+
iv = cipher.random_iv
|
27
|
+
|
28
|
+
cipher.key = key
|
29
|
+
cipher.iv = iv
|
30
|
+
|
31
|
+
encrypted_output = cipher.update(value) + cipher.final
|
32
|
+
|
33
|
+
[
|
34
|
+
Base64.strict_encode64(iv),
|
35
|
+
Base64.strict_encode64(encrypted_output)
|
36
|
+
].join("$")
|
37
|
+
end
|
38
|
+
|
39
|
+
def decrypt
|
40
|
+
iv, encrypted_text = value.split("$")
|
41
|
+
|
42
|
+
decipher = OpenSSL::Cipher::AES256.new :CBC
|
43
|
+
decipher.decrypt
|
44
|
+
|
45
|
+
decipher.iv = Base64.strict_decode64(iv) # previously saved
|
46
|
+
decipher.key = key
|
47
|
+
|
48
|
+
output = decipher.update(Base64.strict_decode64(encrypted_text)) + decipher.final
|
49
|
+
|
50
|
+
output
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :key, :value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
# DELETE ME, USE DATETIME AND NOT TIME CLASS
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Portunus
|
2
|
+
class FieldConfigurer
|
3
|
+
def self.for(object, field)
|
4
|
+
new(object: object, field: field).setup
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(object:, field:)
|
8
|
+
require "pry"
|
9
|
+
@object = object
|
10
|
+
|
11
|
+
if field.is_a?(Hash)
|
12
|
+
@field = field.keys.first.to_sym
|
13
|
+
@field_options = default_field_options.merge(field[field.keys.first])
|
14
|
+
else
|
15
|
+
@field = field
|
16
|
+
@field_options = default_field_options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup
|
21
|
+
# add the field to a configuration so we can easily determine if it's
|
22
|
+
# a field we need to attempt to encrypt and decrypt. We need this in
|
23
|
+
# the `field_before_typecast` method since the normal accessors are
|
24
|
+
# not used after a validation fail
|
25
|
+
register_field
|
26
|
+
|
27
|
+
# setup the methods on the model itself
|
28
|
+
define_instance_methods
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
attr_accessor :field, :field_options, :object
|
34
|
+
|
35
|
+
def default_field_options
|
36
|
+
{
|
37
|
+
type: :string
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def instance_methods_for_model
|
42
|
+
[
|
43
|
+
:define_getter,
|
44
|
+
:define_setter,
|
45
|
+
:define_before_type_cast,
|
46
|
+
:define_association
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_instance_methods
|
51
|
+
instance_methods_for_model.map do |method|
|
52
|
+
send(method)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def define_setter
|
57
|
+
l_field_options = field_options
|
58
|
+
|
59
|
+
# Override the setter of the field to do the encryption
|
60
|
+
object.define_method "#{field}=" do |value, &block|
|
61
|
+
if value.present?
|
62
|
+
dek = data_encryption_key
|
63
|
+
|
64
|
+
encrypted_value = ::Portunus.
|
65
|
+
configuration.
|
66
|
+
encrypter.
|
67
|
+
encrypt(
|
68
|
+
value: Portunus::TypeCaster.cast(
|
69
|
+
value: value,
|
70
|
+
type: l_field_options[:type]
|
71
|
+
),
|
72
|
+
key: dek.key
|
73
|
+
)
|
74
|
+
end
|
75
|
+
|
76
|
+
super(encrypted_value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def define_getter
|
81
|
+
# This is required to force the proper scope in this context.
|
82
|
+
l_field = field
|
83
|
+
l_field_options = field_options
|
84
|
+
|
85
|
+
object.define_method(l_field.to_sym) do
|
86
|
+
value = read_attribute(l_field.to_sym)
|
87
|
+
|
88
|
+
if value.present?
|
89
|
+
dek = data_encryption_key
|
90
|
+
|
91
|
+
decrypted_value = ::Portunus.
|
92
|
+
configuration.
|
93
|
+
encrypter.
|
94
|
+
decrypt(value: value, key: dek.key)
|
95
|
+
|
96
|
+
Portunus::TypeCaster.uncast(
|
97
|
+
value: decrypted_value,
|
98
|
+
type: l_field_options[:type]
|
99
|
+
)
|
100
|
+
else
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def define_before_type_cast
|
107
|
+
l_field = field
|
108
|
+
l_field_options = field_options
|
109
|
+
|
110
|
+
object.define_method "#{l_field}_before_type_cast" do
|
111
|
+
value = super()
|
112
|
+
encrypted = self.class.encrypted_fields_list.include?(l_field.to_sym)
|
113
|
+
|
114
|
+
if encrypted && value.present?
|
115
|
+
dek = data_encryption_key
|
116
|
+
decrypted_value = ::Portunus.
|
117
|
+
configuration.
|
118
|
+
encrypter.
|
119
|
+
decrypt(value: value, key: dek.key)
|
120
|
+
|
121
|
+
value = ::Portunus::TypeCaster.uncast(
|
122
|
+
value: decrypted_value,
|
123
|
+
type: l_field_options[:type]
|
124
|
+
)
|
125
|
+
end
|
126
|
+
|
127
|
+
return value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def define_association
|
132
|
+
# setup a lazy instantiaion of the DEK so that we don't need to worry
|
133
|
+
# about building it for every type of model
|
134
|
+
object.define_method :data_encryption_key do
|
135
|
+
# this is to determine if a data encryption key is present
|
136
|
+
# if not it will lazily create one and fill out the attribute
|
137
|
+
# field on the model containing the key
|
138
|
+
result = super()
|
139
|
+
|
140
|
+
if result.blank?
|
141
|
+
# self here is the model including encryptable. We pass this
|
142
|
+
# so we can call the rails build_data_encryption_key on the
|
143
|
+
# model and set up polymorphic columns automatically
|
144
|
+
dek = ::Portunus::DataKeyGenerator.generate(self)
|
145
|
+
dek
|
146
|
+
else
|
147
|
+
result
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def register_field
|
153
|
+
# Register the field so we can look it up later
|
154
|
+
object.encrypted_fields_list << field
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Portunus
|
2
|
+
class MasterKey
|
3
|
+
include ActiveModel::Model
|
4
|
+
|
5
|
+
attr_accessor :name, :value, :enabled, :created_at
|
6
|
+
|
7
|
+
def self.load
|
8
|
+
Portunus.configuration.storage_adaptor.load
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.lookup(key_name)
|
12
|
+
Portunus.configuration.storage_adaptor.lookup(key_name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# lib/railtie.rb
|
2
|
+
require "portunus"
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
module Portunus
|
6
|
+
class Railtie < Rails::Railtie
|
7
|
+
railtie_name :portunus
|
8
|
+
|
9
|
+
rake_tasks do
|
10
|
+
path = File.expand_path(__dir__)
|
11
|
+
Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Portunus
|
2
|
+
module Rotators
|
3
|
+
class Dek
|
4
|
+
def self.for(data_encryption_key)
|
5
|
+
new(data_encryption_key).rotate
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(data_encryption_key)
|
9
|
+
@data_encryption_key = data_encryption_key
|
10
|
+
end
|
11
|
+
|
12
|
+
def rotate
|
13
|
+
encryptable = data_encryption_key.encryptable
|
14
|
+
|
15
|
+
encryptable.class.encrypted_fields_list.map do |field_name|
|
16
|
+
field_value_map[field_name.to_sym] = encryptable.send(field_name.to_sym)
|
17
|
+
end
|
18
|
+
|
19
|
+
data_encryption_key.encrypted_key = new_encrypted_key
|
20
|
+
|
21
|
+
field_value_map.map do |field_name, value|
|
22
|
+
encryptable.send("#{field_name}=".to_sym, value)
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Base.transaction do
|
26
|
+
encryptable.save
|
27
|
+
data_encryption_key.last_dek_rotation = DateTime.now
|
28
|
+
data_encryption_key.save
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
rescue StandardError => error
|
33
|
+
raise ::Portunus::Error.new(
|
34
|
+
"Rotating DEK failed: #{error.full_message}"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :data_encryption_key
|
41
|
+
|
42
|
+
def storage_adaptor
|
43
|
+
::Portunus.configuration.storage_adaptor
|
44
|
+
end
|
45
|
+
|
46
|
+
def encrypter
|
47
|
+
::Portunus.configuration.encrypter
|
48
|
+
end
|
49
|
+
|
50
|
+
def field_value_map
|
51
|
+
@_field_value_map ||= {}
|
52
|
+
end
|
53
|
+
|
54
|
+
def master_key
|
55
|
+
storage_adaptor.lookup(data_encryption_key.master_keyname)
|
56
|
+
end
|
57
|
+
|
58
|
+
def new_plaintext_key
|
59
|
+
@_new_plaintext_key ||= encrypter.generate_key
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_encrypted_key
|
63
|
+
encrypter.encrypt(
|
64
|
+
key: master_key.value, value: new_plaintext_key
|
65
|
+
)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Portunus
|
2
|
+
module Rotators
|
3
|
+
class Kek
|
4
|
+
def self.for(data_encryption_key)
|
5
|
+
new(data_encryption_key).rotate
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(data_encryption_key)
|
9
|
+
@data_encryption_key = data_encryption_key
|
10
|
+
@unencrypted_dek = data_encryption_key.key
|
11
|
+
end
|
12
|
+
|
13
|
+
def rotate
|
14
|
+
data_encryption_key.master_keyname = new_master_key_name
|
15
|
+
data_encryption_key.encrypted_key = encrypted_dek_with_new_master
|
16
|
+
data_encryption_key.last_kek_rotation = DateTime.now
|
17
|
+
data_encryption_key.save!
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :data_encryption_key, :unencrypted_dek
|
23
|
+
|
24
|
+
def encrypted_dek_with_new_master
|
25
|
+
Portunus.configuration.encrypter.encrypt(
|
26
|
+
key: new_master_key.value,
|
27
|
+
value: unencrypted_dek
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def new_master_key
|
32
|
+
@_new_master_key ||= ::Portunus.configuration.storage_adaptor.lookup(
|
33
|
+
new_master_key_name.to_sym
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def wrapped_current_master_key
|
38
|
+
[data_encryption_key.master_keyname]
|
39
|
+
end
|
40
|
+
|
41
|
+
def master_keys
|
42
|
+
Portunus.configuration.storage_adaptor.list
|
43
|
+
end
|
44
|
+
|
45
|
+
def new_master_key_name
|
46
|
+
@_new_master_key_name ||= (master_keys - wrapped_current_master_key).
|
47
|
+
sample
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Portunus
|
2
|
+
module StorageAdaptors
|
3
|
+
class Credentials
|
4
|
+
def self.for(data_encryption_key)
|
5
|
+
self.lookup(data_encryption_key.master_keyname)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.load
|
9
|
+
key_names = Rails.application.credentials.portunus.keys
|
10
|
+
|
11
|
+
key_names.map do |key_name|
|
12
|
+
::Portunus.configuration.add_key(key_name)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Required method
|
17
|
+
def self.lookup(key_name)
|
18
|
+
master_key = Rails.application.credentials.portunus[key_name.to_sym]
|
19
|
+
|
20
|
+
MasterKey.new(
|
21
|
+
enabled: master_key[:enabled],
|
22
|
+
name: key_name,
|
23
|
+
value: master_key[:key],
|
24
|
+
created_at: master_key[:created_at]
|
25
|
+
)
|
26
|
+
rescue StandardError
|
27
|
+
raise ::Portunus::Error.new("Portunus: Master key not found")
|
28
|
+
end
|
29
|
+
|
30
|
+
# Required method
|
31
|
+
def self.list
|
32
|
+
key_names = Rails.application.credentials.portunus.keys
|
33
|
+
# reject any disabled keys so we no longer utilize them for new
|
34
|
+
# deks
|
35
|
+
key_names.reject! do |key_name|
|
36
|
+
Rails.application.credentials.portunus[key_name][:enabled] == false
|
37
|
+
end
|
38
|
+
|
39
|
+
if key_names.length == 0
|
40
|
+
raise ::Portunus::Error.new("No valid master keys configured")
|
41
|
+
end
|
42
|
+
|
43
|
+
key_names
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Portunus
|
2
|
+
module StorageAdaptors
|
3
|
+
class Environment
|
4
|
+
def self.for(data_encryption_key)
|
5
|
+
self.lookup(data_encryption_key.master_keyname)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.key_map
|
9
|
+
@@key_map ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.reset_key_map
|
13
|
+
@@key_map = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.load
|
17
|
+
key_names = ENV.keys.select { |key| key.start_with?("PORTUNUS_") }
|
18
|
+
|
19
|
+
key_names.map do |name|
|
20
|
+
_portunus, key_name, key_type = name.split("_")
|
21
|
+
|
22
|
+
if self.key_map[key_name.to_sym].blank?
|
23
|
+
self.key_map[key_name.to_sym] = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
self.key_map[key_name.to_sym][key_type.to_sym] = ENV.fetch(name)
|
27
|
+
end
|
28
|
+
|
29
|
+
true
|
30
|
+
rescue StandardError => error
|
31
|
+
raise ::Portunus::Error.new(
|
32
|
+
"Portunus: Master keys failed to load: #{error.full_message}"
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.lookup(key_name)
|
37
|
+
master_key = self.key_map[key_name.to_sym]
|
38
|
+
|
39
|
+
MasterKey.new(
|
40
|
+
enabled: master_key[:ENABLED],
|
41
|
+
name: key_name,
|
42
|
+
value: master_key[:KEY],
|
43
|
+
created_at: master_key[:CREATED]
|
44
|
+
)
|
45
|
+
rescue StandardError
|
46
|
+
raise ::Portunus::Error.new("Portunus: Master key not found")
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.list
|
50
|
+
# Select only enabled keys
|
51
|
+
key_names = self.key_map.keys.map do |keyname|
|
52
|
+
keyname if self.key_map[keyname][:ENABLED] == "true"
|
53
|
+
end.compact
|
54
|
+
|
55
|
+
if key_names.length == 0
|
56
|
+
raise ::Portunus::Error.new("No valid master keys configured")
|
57
|
+
end
|
58
|
+
|
59
|
+
key_names
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
attr_reader :data_encryption_key
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|