Module: Pct

Defined in:
lib/pct.rb,
lib/pct/version.rb

Defined Under Namespace

Classes: Decryptable, Error

Constant Summary collapse

DEFAULT =
<<~EOS
  configuration:
    example_key: "some value that should be encrypted"
    some:
      nested: "values"

  key_mapping:
    example_key: 'SomeKmsKeyAlias'
    some:
      nested: 'SomeKmsKeyAlias'
EOS
VERSION =
"0.7.2"

Class Method Summary collapse

Class Method Details

.clientObject



34
35
36
# File 'lib/pct.rb', line 34

def self.client
  @client ||= Aws::KMS::Client.new
end

.create(path) ⇒ Object



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/pct.rb', line 87

def self.create(path)
  file = Tempfile.new(['new-config', '.yml'])

  File.open(file.path, 'w') do |f|
    f.puts(DEFAULT)
  end

  system("#{ENV['EDITOR']} #{file.path}")

  new_config = YAML.load(File.read(file.path))
  encrypted_config = to_encrypted(new_config['configuration'], new_config['key_mapping'])

  File.open(path, 'w') do |f|
    f.write(encrypted_config.to_yaml)
  end
ensure
  file.close
  file.unlink
end

.edit(path, decrypt) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/pct.rb', line 127

def self.edit(path, decrypt)
  file = Tempfile.new([path, '.yml'])

  processed_file = YAML.load(File.read(path))
  original_file = YAML.load(File.read(path))

  if decrypt
    processed_file['configuration'] = to_decrypted(processed_file['configuration'])
  else
    processed_file['configuration'] = set_placeholders(processed_file['configuration'])
  end

  File.open(file.path, 'w') do |f|
    f.puts(processed_file.to_yaml)
  end

  system("#{ENV['EDITOR']} #{file.path}")

  new_config = YAML.load(File.read(file.path))
  encrypted_config = to_encrypted(new_config['configuration'], new_config['key_mapping'], original_file['configuration'], processed_file['configuration'])

  File.open(path, 'w') do |f|
    f.write(encrypted_config.to_yaml)
  end
ensure
  file.close
  file.unlink
end

.encrypt(value, key) ⇒ Object

Raises:

  • (ArgumentError)


53
54
55
56
57
58
59
60
# File 'lib/pct.rb', line 53

def self.encrypt(value, key)
  raise ArgumentError, "all values for encryption must be a string" if !value.is_a?(String)

  Base64.strict_encode64(client.encrypt({
    key_id: "alias/#{key}",
    plaintext: value
  }).ciphertext_blob)
end

.load_config(path) ⇒ Object



38
39
40
41
# File 'lib/pct.rb', line 38

def self.load_config(path)
  config = YAML.load(File.read(path))
  to_usable(config['configuration'])
end

.set_placeholders(config) ⇒ Object



107
108
109
110
111
112
113
114
115
# File 'lib/pct.rb', line 107

def self.set_placeholders(config)
  config.each do |k, v|
    if v.is_a?(Hash)
      set_placeholders(v)
    else
      config[k] = '<encrypted>'
    end
  end
end

.to_decrypted(configuration) ⇒ Object



117
118
119
120
121
122
123
124
125
# File 'lib/pct.rb', line 117

def self.to_decrypted(configuration)
  Hash[configuration.map do |k, v|
    if v.is_a?(Hash)
      [k, to_decrypted(v)]
    else
      [k, client.decrypt({ciphertext_blob: Base64.decode64(v)}).plaintext]
    end
  end]
end

.to_encrypted(configuration, key_mapping, original_config = {}, potentially_decrypted = {}) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/pct.rb', line 62

def self.to_encrypted(configuration, key_mapping, original_config = {}, potentially_decrypted = {})
  encrypted_config = Hash[configuration.map do |key, value|
    if value.is_a?(Hash)
      [key, to_encrypted(value, key_mapping[key], original_config[key] || {}, potentially_decrypted || {})['configuration']]
    else
      if value == '<encrypted>'
        if !original_config[key]
          raise "moving encrypted values is not supported"
        end

        [key, original_config[key]]
      elsif value == potentially_decrypted[key]
        [key, original_config[key]]
      else
        [key, encrypt(value, key_mapping[key])]
      end
    end
  end]

  {
    'configuration' => encrypted_config,
    'key_mapping' => key_mapping,
  }
end

.to_usable(configuration) ⇒ Object



43
44
45
46
47
48
49
50
51
# File 'lib/pct.rb', line 43

def self.to_usable(configuration)
  Hash[configuration.map do |k, v|
    if v.is_a?(Hash)
      [k, to_usable(v)]
    else
      [k, Decryptable.new(v)]
    end
  end]
end