Class: SecretKeys

Inherits:
Hash
  • Object
show all
Defined in:
lib/secret_keys.rb,
lib/secret_keys/version.rb

Overview

Load a JSON file with encrypted values. This value can be used as a hash.

Defined Under Namespace

Modules: CLI Classes: EncryptionKeyError, Encryptor, VersionError

Constant Summary collapse

VERSION =
File.read(File.join(__dir__, "..", "..", "VERSION")).chomp.freeze
CRYPTO_VERSION =
1

Instance Method Summary collapse

Constructor Details

#initialize(path_or_stream, encryption_key = nil) ⇒ SecretKeys

Note:

If no encryption key is passed, this will defautl to env var SECRET_KEYS_ENCRYPTION_KEY

Parse a JSON or YAML stream or file with encrypted values. Any values in the “.encrypted” key in the document will be decrypted with the provided encryption key. If values were put into the “.encrypted” key manually and are not yet encrypted, they will be used as is without any decryption.

or (if that is empty) the value read from the file path in SECRET_KEYS_ENCRYPTION_KEY_FILE.

Parameters:

  • path_or_stream (String, #read, Hash)

    path to a JSON/YAML file to load, an IO object, or a Hash (mostly for testing purposes)

  • encryption_key (String) (defaults to: nil)

    secret to use for encryption/decryption



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/secret_keys.rb', line 28

def initialize(path_or_stream, encryption_key = nil)
  @encryption_key = nil
  @salt = nil
  @format = :json

  encryption_key = read_encryption_key(encryption_key)
  update_secret(key: encryption_key)
  path_or_stream = Pathname.new(path_or_stream) if path_or_stream.is_a?(String)
  load_secrets!(path_or_stream)

  super(@values)
end

Instance Method Details

#decrypt!(key) ⇒ void

This method returns an undefined value.

Mark the key as no longer being decrypted when the JSON is saved.

Parameters:

  • key (String)

    key to mark as not needing encryption



62
63
64
65
# File 'lib/secret_keys.rb', line 62

def decrypt!(key)
  @secret_keys.delete(key)
  nil
end

#encrypt!(key) ⇒ void

This method returns an undefined value.

Mark the key as being encrypted when the JSON is saved.

Parameters:

  • key (String)

    key to mark as needing encryption



53
54
55
56
# File 'lib/secret_keys.rb', line 53

def encrypt!(key)
  @secret_keys << key
  nil
end

#encrypted?(key) ⇒ Boolean

Return true if the key is encrypted.

Parameters:

  • key (String)

    key to check

Returns:

  • (Boolean)


71
72
73
# File 'lib/secret_keys.rb', line 71

def encrypted?(key)
  @secret_keys.include?(key)
end

#encrypted_hashHash

Output the keys as a hash that matches the structure that can be loaded by the initalizer. Values that have not changed will not be re-salted so the encrypted values will remain the same.

Returns:

  • (Hash)

    An encrypted hash that can be saved/parsed by a new instance of SecretKeys

Raises:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/secret_keys.rb', line 107

def encrypted_hash
  raise EncryptionKeyError.new("Encryption key not specified") if @encryption_key.nil? || @encryption_key.empty?

  hash = {}
  encrypted = {}
  @values.each do |key, value|
    if @secret_keys.include?(key)
      encrypted[key] = value
    else
      hash[key] = value
    end
  end

  unless encryption_key_matches?(@original_encrypted[ENCRYPTION_KEY])
    @original_encrypted = {}
  end
  encrypted.merge!(encrypt_values(encrypted, @original_encrypted))
  encrypted[SALT] = @salt
  encrypted[ENCRYPTION_KEY] = (@original_encrypted[ENCRYPTION_KEY] || encrypted_known_value)
  encrypted[VERSION_KEY] = CRYPTO_VERSION

  hash[ENCRYPTED] = encrypted
  hash
end

#encryption_key=(new_encryption_key) ⇒ void

This method returns an undefined value.

Change the encryption key in the document. When saving later, this key will be used.

Parameters:

  • new_encryption_key (String)

    encryption key to use for future #save calls



136
137
138
139
# File 'lib/secret_keys.rb', line 136

def encryption_key=(new_encryption_key)
  @original_encrypted = {}
  update_secret(key: new_encryption_key)
end

#input_formatString

Return the data format (:json or :yaml) for the original data. Defaults to :json.

Returns:

  • (String)


144
145
146
# File 'lib/secret_keys.rb', line 144

def input_format
  @format
end

#save(path, format: nil) ⇒ void

This method returns an undefined value.

Save the encrypted hash to a file at the specified path. Encrypted values in an existing file will not be updated if the values have not changed (since each call uses a different initialization vector). This can be helpful if you have your secrets in source control so that only changed keys will actually be changed in the file when it is updated.

Parameters:

  • path (String, Pathname)

    path of the file to save. If the file exists, only changed values will be updated.

  • format: (String, Symbol) (defaults to: nil)

    output format (YAML or JSON) to use. This will default based on the extension on the file path or the format originally used



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/secret_keys.rb', line 83

def save(path, format: nil)
  # create a copy of the encrypted hash for working on
  encrypted = encrypted_hash

  if format.nil?
    if yaml_file?(path)
      format = :yaml
    elsif json_file?(path)
      format = :json
    end
  end
  format ||= @format
  format = format.to_s.downcase

  output = ((format == "yaml") ? YAML.dump(encrypted) : JSON.pretty_generate(encrypted))
  output << $/ unless output.end_with?($/) # ensure file ends with system dependent new line
  File.write(path, output)
  nil
end

#to_hHash Also known as: to_hash

Convert into an actual Hash object.

Returns:

  • (Hash)


44
45
46
# File 'lib/secret_keys.rb', line 44

def to_h
  @values
end