lib/symmetric_encryption/config.rb in symmetric-encryption-3.9.1 vs lib/symmetric_encryption/config.rb in symmetric-encryption-4.0.0.beta3

- old
+ new

@@ -1,74 +1,96 @@ +require 'erb' +require 'yaml' module SymmetricEncryption - module Config - # Load the Encryption Configuration from a YAML file - # filename: - # Name of file to read. - # Mandatory for non-Rails apps - # Default: Rails.root/config/symmetric-encryption.yml - # environment: - # Which environments config to load. Usually: production, development, etc. - # Default: Rails.env - def self.load!(filename=nil, environment=nil) - config = read_config(filename, environment) - ciphers = extract_ciphers(config) + class Config + attr_reader :file_name, :env + # Load the Encryption Configuration from a YAML file. + # + # file_name: + # Name of configuration file. + # Default: "#{Rails.root}/config/symmetric-encryption.yml" + # Note: + # The Symmetric Encryption config file name can also be set using the `SYMMETRIC_ENCRYPTION_CONFIG` + # environment variable. + # + # env: + # Which environments config to load. Usually: production, development, etc. + # Non-Rails apps can set env vars: RAILS_ENV, or RACK_ENV + # Default: Rails.env || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' + def self.load!(file_name: nil, env: nil) + config = new(file_name: file_name, env: env) + ciphers = config.ciphers SymmetricEncryption.cipher = ciphers.shift SymmetricEncryption.secondary_ciphers = ciphers true end - private + # Reads the entire configuration for all environments from the supplied file name. + def self.read_file(file_name) + config = YAML.load(ERB.new(File.new(file_name).read).result) + config = deep_symbolize_keys(config) + config.each_pair { |env, cfg| SymmetricEncryption::Config.send(:migrate_old_formats!, cfg) } + config + end - # Returns [Hash] the configuration for the supplied environment - def self.read_config(filename=nil, environment=nil) - config_filename = filename || File.join(Rails.root, 'config', 'symmetric-encryption.yml') - cfg = YAML.load(ERB.new(File.new(config_filename).read).result)[environment || Rails.env] - extract_config(cfg) + # Write the entire configuration for all environments to the supplied file name. + def self.write_file(file_name, config) + config = deep_stringify_keys(config) + File.open(file_name, 'w') do |f| + f.puts '# This file was auto generated by symmetric-encryption.' + f.puts '# Recommend using symmetric-encryption to make changes.' + f.puts '# For more info, run:' + f.puts '# symmetric-encryption --help' + f.puts '#' + f.write(config.to_yaml) + end end - # Returns [ private_rsa_key, ciphers ] config - def self.extract_config(config) - config = deep_symbolize_keys(config) + # Load the Encryption Configuration from a YAML file. + # + # See: `.load!` for parameters. + def initialize(file_name: nil, env: nil) + unless env + env = defined?(Rails) ? Rails.env : ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' + end - # Old format? - unless config.has_key?(:ciphers) - config = { - private_rsa_key: config.delete(:private_rsa_key), - ciphers: [config] - } + unless file_name + root = defined?(Rails) ? Rails.root : '.' + file_name = + if env_var = ENV['SYMMETRIC_ENCRYPTION_CONFIG'] + File.expand_path(env_var) + else + File.join(root, 'config', 'symmetric-encryption.yml') + end + raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name) end - # Old format cipher name? - config[:ciphers] = config[:ciphers].collect do |cipher| - if old_key_name_cipher = cipher.delete(:cipher) - cipher[:cipher_name] = old_key_name_cipher + @env = env + @file_name = file_name + end + + # Returns [Hash] the configuration for the supplied environment. + def config + @config ||= begin + raise(ConfigError, "Cannot find config file: #{file_name}") unless File.exist?(file_name) + unless env_config = YAML.load(ERB.new(File.new(file_name).read).result)[env] + raise(ConfigError, "Cannot find environment: #{env} in config file: #{file_name}") end - cipher + env_config = self.class.deep_symbolize_keys(env_config) + self.class.migrate_old_formats!(env_config) end - config end - # Returns [Array(SymmetricEncrytion::Cipher)] ciphers specified in the configuration file - # - # Read the configuration from the YAML file and return in the latest format - # - # filename: - # Name of file to read. - # Mandatory for non-Rails apps - # Default: Rails.root/config/symmetric-encryption.yml - # environment: - # Which environments config to load. Usually: production, development, etc. - def self.extract_ciphers(config) - private_rsa_key = config[:private_rsa_key] - - config[:ciphers].collect do |cipher_config| - Cipher.new({private_rsa_key: private_rsa_key}.merge(cipher_config)) - end + # Returns [Array(SymmetricEncrytion::Cipher)] ciphers specified in the configuration file. + def ciphers + @ciphers ||= config[:ciphers].collect { |cipher_config| Cipher.from_config(cipher_config) } end - # Iterate through the Hash symbolizing all keys + private + + # Iterate through the Hash symbolizing all keys. def self.deep_symbolize_keys(x) case x when Hash result = {} x.each_pair do |key, value| @@ -79,9 +101,65 @@ when Array x.collect { |i| deep_symbolize_keys(i) } else x end + end + + # Iterate through the Hash symbolizing all keys. + def self.deep_stringify_keys(x) + case x + when Hash + result = {} + x.each_pair do |key, value| + key = key.to_s if key.is_a?(Symbol) + result[key] = deep_stringify_keys(value) + end + result + when Array + x.collect { |i| deep_stringify_keys(i) } + else + x + end + end + + # Migrate old configuration format for this environment + def self.migrate_old_formats!(config) + # Inline single cipher before :ciphers + unless config.has_key?(:ciphers) + cipher = {} + config.keys.each { |key| cipher[key] = config.delete(key) } + config[:ciphers] = [cipher] + end + + # Copy Old :private_rsa_key into each ciphers config + # Cipher.from_config replaces it with the RSA Kek + if config[:private_rsa_key] + private_rsa_key = config.delete(:private_rsa_key) + config[:ciphers].each { |cipher| cipher[:private_rsa_key] = private_rsa_key } + end + + # Old :cipher_name + config[:ciphers].each do |cipher| + if old_key_name_cipher = cipher.delete(:cipher) + cipher[:cipher_name] = old_key_name_cipher + end + + # Only temporarily used during v4 Beta process + if cipher[:key_encrypting_key].is_a?(String) + cipher[:private_rsa_key] = cipher.delete(:key_encrypting_key) + end + + # Check for a prior env var in encrypted key + # Example: + # encrypted_key: <%= ENV['VAR'] %> + if cipher.has_key?(:encrypted_key) && cipher[:encrypted_key].nil? + cipher[:key_env_var] = :placeholder + puts "WARNING: :encrypted_key resolved to nil. Please see the migrated config file for the new option :key_env_var." + end + + end + config end end end