portunus 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|