diffcrypt 0.3.1 → 0.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e921781508e8ff365d5ff49e6ad151176b40234d8ef96e1df9e0ad8df6f35cc8
4
- data.tar.gz: 992ef8b7850df1cf3588270b12d7c325756df1380723b2c786a9bcda92d60886
3
+ metadata.gz: dacd30e3ddee50fd84847385d2cd4161645b71b1cda79566557dff60e1f15108
4
+ data.tar.gz: 648e5cf57c6b909cf84196ded1643aa41baf776b8428862d57804d6465ebb3f0
5
5
  SHA512:
6
- metadata.gz: ebc1f0b8754cd2ef58143ed234e0bd350bf9206dfe852d977ca671f84fc7100b757ab05e5146992105a535f8b2e99fbfbfc9ebe18d97e760035bede4fdbd35a5
7
- data.tar.gz: 9a0e86a6332e9bfaa31ab0392de4f453fc48c84e6528068fddca637eb8785024e25704cbfee3a5534254fcf042c6ba5407781f04b32fbae2a183d46c6e7be1fb
6
+ metadata.gz: 8c20572e67d7416fd2fac81556eadba73555959273f7ac900a2d05c82c4799135edb9940a69659eca121cddfee4dd99b3a1b94e7b5effeeea491677ae638b2b7
7
+ data.tar.gz: 7e8fe375714cdfcb730792285f648a8a7f1164fe9015b42ee06a0a592b065a576a4059ba0d338f9ec79a0749b087d186c73e1d5ba89c6cecaebb4123490eabdf
@@ -1,11 +1,78 @@
1
1
  version: 2.1
2
2
 
3
3
  jobs:
4
- build:
4
+ bundle:
5
5
  docker:
6
- - image: circleci/ruby:2.6.6
6
+ - image: circleci/ruby:2.7.1
7
+ working_directory: /mnt/ramdisk
7
8
  steps:
8
9
  - checkout
9
- - run: bundle install
10
- - run: bundle exec rake test
11
- - run: bundle exec rubocop
10
+ - restore_cache:
11
+ keys:
12
+ - bundler-{{ checksum "diffcrypt.gemspec" }}-{{ .Environment.CACHE_VERSION }}
13
+ - run:
14
+ name: bundle install
15
+ command: |
16
+ gem install bundler -v '2.1.4'
17
+ bundle install --path=vendor/bundle --jobs=4 --retry=3
18
+ - save_cache:
19
+ key: bundler-{{ checksum "diffcrypt.gemspec" }}-{{ .Environment.CACHE_VERSION }}
20
+ paths:
21
+ - vendor/bundle
22
+
23
+ test:
24
+ docker:
25
+ - image: circleci/ruby:2.7.1
26
+ working_directory: /mnt/ramdisk
27
+ steps:
28
+ - checkout
29
+ - restore_cache:
30
+ keys:
31
+ - bundler-{{ checksum "diffcrypt.gemspec" }}-{{ .Environment.CACHE_VERSION }}
32
+ - run:
33
+ name: bundle install
34
+ command: |
35
+ gem install bundler -v '2.1.4'
36
+ bundle install --path=vendor/bundle --jobs=4 --retry=3
37
+ - run:
38
+ name: Setup Code Climate test-reporter
39
+ command: |
40
+ # download test reporter as a static binary
41
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
42
+ chmod +x ./cc-test-reporter
43
+ - run:
44
+ name: rake test
45
+ command: |
46
+ ./cc-test-reporter before-build
47
+ bundle exec rake test
48
+ ./cc-test-reporter after-build --coverage-input-type lcov --exit-code $?
49
+ rubocop:
50
+ docker:
51
+ - image: circleci/ruby:2.7.1
52
+ working_directory: /mnt/ramdisk
53
+ steps:
54
+ - checkout
55
+ - restore_cache:
56
+ keys:
57
+ - bundler-{{ checksum "diffcrypt.gemspec" }}-{{ .Environment.CACHE_VERSION }}
58
+ - run:
59
+ name: bundle install
60
+ command: |
61
+ gem install bundler -v '2.1.4'
62
+ bundle install --path=vendor/bundle --jobs=4 --retry=3
63
+ - run:
64
+ name: rubocop
65
+ command: bundle exec rubocop
66
+ when: always
67
+
68
+ workflows:
69
+ version: 2
70
+ all:
71
+ jobs:
72
+ - bundle
73
+ - test:
74
+ requires:
75
+ - bundle
76
+ - rubocop:
77
+ requires:
78
+ - bundle
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  NewCops: enable
3
+ TargetRubyVersion: 2.6
3
4
 
4
5
  Style/ClassAndModuleChildren:
5
6
  Exclude:
@@ -9,8 +10,14 @@ Style/Documentation:
9
10
  Metrics/MethodLength:
10
11
  Exclude:
11
12
  - test/**/*_test.rb
13
+ Style/TrailingCommaInArrayLiteral:
14
+ EnforcedStyleForMultiline: consistent_comma
12
15
  Style/TrailingCommaInArguments:
13
16
  EnforcedStyleForMultiline: consistent_comma
17
+ Style/TrailingCommaInHashLiteral:
18
+ EnforcedStyleForMultiline: consistent_comma
19
+ Style/AccessorGrouping:
20
+ EnforcedStyle: separated
14
21
 
15
22
  Layout/LineLength:
16
23
  Exclude:
@@ -7,6 +7,68 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
 
9
9
 
10
+ ## [0.5.0] - 2020-12-06
11
+
12
+ ### Added
13
+
14
+ - `Diffcrypt::Rails::ApplicationHelper` to simplify integration
15
+ - `rake diffcrypt:init` command to help setup credentials
16
+
17
+ ### Changed
18
+
19
+ - Default cipher is now `aes-265-gcm`
20
+ - YAML keys are now sorted
21
+ - Improved support for rails native `aes-128-gcm` cipher
22
+ - Improved test coverage for differing ciphers
23
+
24
+
25
+
26
+ ## [0.4.1] - 2020-10-06
27
+
28
+ ### Fixed
29
+
30
+ - Could not initialize a new file or modify existing rails format file (Thanks @swiknaba #31)
31
+
32
+
33
+
34
+ ## [0.4.0] - 2020-10-01
35
+
36
+ ### Changed
37
+
38
+ - Encryptor can now use other ciphers than the default
39
+
40
+ ### Dependencies
41
+
42
+ - simplecov: 0.17.0 -> 0.18.0 (#20)
43
+ - rubocop: 0.88.0 -> 0.92.0 (#24)
44
+
45
+
46
+
47
+ ## [0.3.3] - 2020-07-25
48
+
49
+ ### Fixed
50
+
51
+ - Explicit FileUtils require to avoid potentially warning logs
52
+
53
+
54
+
55
+ ## [0.3.2] - 2020-07-20
56
+
57
+ ### Added
58
+
59
+ - CLI: `diffcrypt generate-key` command to generate a new key for a cipher
60
+ - Internal: Library now generates and publishes code coverage publically on Code Climate
61
+
62
+ ### Changed
63
+
64
+ - Only support ruby 2.5+ since 2.4 is no longer maintained
65
+
66
+ ### Removed
67
+
68
+ - No longer generate and store a checksum. Backwards compatible since it wasn't used
69
+
70
+
71
+
10
72
  ## [0.3.1] - 2020-07-08
11
73
 
12
74
  ### Fixed
data/Gemfile CHANGED
@@ -7,4 +7,6 @@ gemspec
7
7
 
8
8
  gem 'minitest', '~> 5.0'
9
9
  gem 'rake', '~> 13.0'
10
- gem 'rubocop', '~> 0.86'
10
+ gem 'rubocop', '~> 1.5.2'
11
+ gem 'simplecov', '~> 0.20.0', require: false # CodeClimate not compatible with 0.18+ yet - https://github.com/codeclimate/test-reporter/issues/413
12
+ gem 'simplecov-lcov', '< 0.8'
data/README.md CHANGED
@@ -36,8 +36,8 @@ There are a few ways to use the library, depending on how advanced your use case
36
36
  The easiest way to get started is to use the CLI.
37
37
 
38
38
  ```shell
39
- diffcrypt decrypt -k $(cat test/fixtures/master.key) test/fixtures/example.yml.enc
40
- diffcrypt encrypt -k $(cat test/fixtures/master.key) test/fixtures/example.yml
39
+ diffcrypt decrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml.enc
40
+ diffcrypt encrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml
41
41
  ```
42
42
 
43
43
 
@@ -66,23 +66,27 @@ Currently there is not native support for rails, but ActiveSupport can be monkey
66
66
  the built in encrypter. All existing `rails credentials:edit` also work with this method.
67
67
 
68
68
  ```ruby
69
- require 'diffcrypt/rails/encrypted_configuration'
69
+ # config/application.rb
70
70
  module Rails
71
71
  class Application
72
- def encrypted(path, key_path: 'config/master.key', env_key: 'RAILS_MASTER_KEY')
73
- Diffcrypt::Rails::EncryptedConfiguration.new(
74
- config_path: Rails.root.join(path),
75
- key_path: Rails.root.join(key_path),
76
- env_key: env_key,
77
- raise_if_missing_key: config.require_master_key,
78
- )
79
- end
72
+ include Diffcrypt::Rails::ApplicationHelper
80
73
  end
81
74
  end
82
75
  ```
83
76
 
84
77
 
85
78
 
79
+ ## Converting between ciphers
80
+
81
+ Sometimes you may want to rotate the cipher used on a file. You can do this programmatically using the ruby code above, or you can also chain the CLI commands like so:
82
+
83
+ ```shell
84
+ diffcrypt decrypt -k $(cat test/fixtures/aes-128-gcm.key) test/fixtures/example.yml.enc > test/fixtures/example.128.yml \
85
+ && diffcrypt encrypt --cipher aes-256-gcm -k $(cat test/fixtures/aes-256-gcm.key) test/fixtures/example.128.yml > test/fixtures/example.256.yml.enc && rm test/fixtures/example.128.yml
86
+ ```
87
+
88
+
89
+
86
90
  ## Development
87
91
 
88
92
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/Rakefile CHANGED
@@ -8,5 +8,7 @@ Rake::TestTask.new(:test) do |t|
8
8
  t.libs << 'lib'
9
9
  t.test_files = FileList['test/**/*_test.rb']
10
10
  end
11
-
12
11
  task default: :test
12
+
13
+ path = File.expand_path(__dir__)
14
+ Dir.glob("#{path}/lib/diffcrypt/tasks/**/*.rake").sort.each { |f| load f }
@@ -0,0 +1,17 @@
1
+ # Security Policy
2
+
3
+
4
+
5
+ ## Supported Versions
6
+
7
+ Since the internal APIs may change dramatically until v1.0, here is a list of the versions that are supported.
8
+
9
+ | Version | Supported |
10
+ | ------- | ------------------ |
11
+ | 0.3.x | :white_check_mark: |
12
+
13
+
14
+
15
+ ## Reporting a Vulnerability
16
+
17
+ Please email security@marcqualie.com to report any security issues.
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = 'Diffable encrypted configuration files that can be safely committed into a git repository'
13
13
  spec.homepage = 'https://github.com/marcqualie/diffcrypt'
14
14
  spec.license = 'MIT'
15
- spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
15
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.6.0')
16
16
 
17
17
  # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
18
18
 
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  # Specify which files should be added to the gem when it is released.
24
24
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
- spec.files = Dir.chdir(File.expand_path(__dir__)) do
25
+ spec.files = Dir.chdir(::File.expand_path(__dir__)) do
26
26
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
27
  end
28
28
  spec.bindir = 'bin'
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'diffcrypt/encryptor'
4
4
  require 'diffcrypt/version'
5
+ require 'diffcrypt/railtie' if defined?(Rails)
5
6
 
6
7
  module Diffcrypt
7
8
  class Error < StandardError; end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative './encryptor'
4
+ require_relative './file'
4
5
  require_relative './version'
5
6
 
6
7
  module Diffcrypt
@@ -8,17 +9,24 @@ module Diffcrypt
8
9
  desc 'decrypt <path>', 'Decrypt a file'
9
10
  method_option :key, aliases: %i[k], required: true
10
11
  def decrypt(path)
11
- ensure_file_exists(path)
12
- contents = File.read(path)
13
- puts encryptor.decrypt(contents)
12
+ file = File.new(path)
13
+ ensure_file_exists(file)
14
+ say file.decrypt(key)
14
15
  end
15
16
 
16
17
  desc 'encrypt <path>', 'Encrypt a file'
17
18
  method_option :key, aliases: %i[k], required: true
19
+ method_option :cipher, default: Encryptor::DEFAULT_CIPHER
18
20
  def encrypt(path)
19
- ensure_file_exists(path)
20
- contents = File.read(path)
21
- puts encryptor.encrypt(contents)
21
+ file = File.new(path)
22
+ ensure_file_exists(file)
23
+ say file.encrypt(key, cipher: options[:cipher])
24
+ end
25
+
26
+ desc 'generate-key', 'Generate a 32 bit key'
27
+ method_option :cipher, default: Encryptor::DEFAULT_CIPHER
28
+ def generate_key
29
+ say Encryptor.generate_key(options[:cipher])
22
30
  end
23
31
 
24
32
  desc 'version', 'Show client version'
@@ -35,8 +43,9 @@ module Diffcrypt
35
43
  @encryptor ||= Encryptor.new(key)
36
44
  end
37
45
 
38
- def ensure_file_exists(path)
39
- abort('[ERROR] File does not exist') unless File.exist?(path)
46
+ # @param [Diffcrypt::File] path
47
+ def ensure_file_exists(file)
48
+ abort('[ERROR] File does not exist') unless file.exists?
40
49
  end
41
50
 
42
51
  def self.exit_on_failure?
@@ -12,15 +12,16 @@ require_relative './version'
12
12
 
13
13
  module Diffcrypt
14
14
  class Encryptor
15
- CIPHER = 'aes-128-gcm'
15
+ DEFAULT_CIPHER = 'aes-256-gcm'
16
16
 
17
- def self.generate_key
18
- SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(CIPHER))
17
+ def self.generate_key(cipher = DEFAULT_CIPHER)
18
+ SecureRandom.hex(ActiveSupport::MessageEncryptor.key_len(cipher))
19
19
  end
20
20
 
21
- def initialize(key)
21
+ def initialize(key, cipher: DEFAULT_CIPHER)
22
22
  @key = key
23
- @encryptor ||= ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: CIPHER)
23
+ @cipher = cipher
24
+ @encryptor ||= ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: cipher)
24
25
  end
25
26
 
26
27
  # @param [String] contents The raw YAML string to be encrypted
@@ -46,12 +47,11 @@ module Diffcrypt
46
47
  # @param [String] contents The raw YAML string to be encrypted
47
48
  # @param [String, nil] original_encrypted_contents The original (encrypted) content to determine which keys have changed
48
49
  # @return [String]
49
- def encrypt(contents, original_encrypted_contents = nil)
50
+ def encrypt(contents, original_encrypted_contents = nil, cipher: nil)
50
51
  data = encrypt_data contents, original_encrypted_contents
51
52
  YAML.dump(
52
53
  'client' => "diffcrypt-#{Diffcrypt::VERSION}",
53
- 'cipher' => CIPHER,
54
- 'checksum' => Digest::MD5.hexdigest(Marshal.dump(data)),
54
+ 'cipher' => cipher || @cipher,
55
55
  'data' => data,
56
56
  )
57
57
  end
@@ -86,7 +86,7 @@ module Diffcrypt
86
86
  key_changed ? encrypt_string(value) : original_encrypted_value
87
87
  end
88
88
  end
89
- data
89
+ data.sort.to_h
90
90
  end
91
91
  # rubocop:enable Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
92
92
 
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './encryptor'
4
+
5
+ module Diffcrypt
6
+ class File
7
+ attr_reader :file
8
+
9
+ def initialize(path)
10
+ @path = ::File.absolute_path path
11
+ end
12
+
13
+ def encrypted?
14
+ to_yaml['cipher']
15
+ end
16
+
17
+ # Determines the cipher to use for encryption/decryption
18
+ def cipher
19
+ return 'aes-128-gcm' if format == 'activesupport'
20
+
21
+ to_yaml['cipher'] || Encryptor::DEFAULT_CIPHER
22
+ end
23
+
24
+ # @return [Boolean]
25
+ def exists?
26
+ ::File.exist?(@path)
27
+ end
28
+
29
+ # Determines the format to be used for encryption
30
+ # @return [String] diffcrypt|activesupport
31
+ def format
32
+ return 'diffcrypt' if read == ''
33
+ return 'diffcrypt' if read.index('---')&.zero?
34
+
35
+ 'activesupport'
36
+ end
37
+
38
+ # @return [String] Raw contents of the file
39
+ def read
40
+ return '' unless ::File.exist?(@path)
41
+
42
+ @read ||= ::File.read(@path)
43
+ @read
44
+ end
45
+
46
+ # Save the encrypted contents back to disk
47
+ # @return [Boolean] True is file save was successful
48
+ def write(key, data, cipher: nil)
49
+ cipher ||= self.cipher
50
+ yaml = ::YAML.dump(data)
51
+ contents = Encryptor.new(key, cipher: cipher).encrypt(yaml)
52
+ ::File.write(@path, contents)
53
+ end
54
+
55
+ # TODO: This seems useless, figure out what's up
56
+ def encrypt(key, cipher: DEFAULT_CIPHER)
57
+ return read if encrypted?
58
+
59
+ Encryptor.new(key, cipher: cipher).encrypt(read)
60
+ end
61
+
62
+ # TODO: Add a test to verify this does descrypt properly
63
+ def decrypt(key)
64
+ return read unless encrypted?
65
+
66
+ Encryptor.new(key, cipher: cipher).decrypt(read)
67
+ end
68
+
69
+ def to_yaml
70
+ @to_yaml ||= YAML.safe_load(read) || {}
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './encrypted_configuration'
4
+
5
+ module Diffcrypt
6
+ module Rails
7
+ module ApplicationHelper
8
+ def encrypted(path, key_path: 'config/master.key', env_key: 'RAILS_MASTER_KEY')
9
+ config_path, key_path = resolve_encrypted_paths(path, key_path)
10
+
11
+ Diffcrypt::Rails::EncryptedConfiguration.new(
12
+ config_path: config_path,
13
+ key_path: key_path,
14
+ env_key: env_key,
15
+ raise_if_missing_key: config.require_master_key,
16
+ )
17
+ end
18
+
19
+ protected
20
+
21
+ def resolve_encrypted_paths(config_path, key_path)
22
+ config_path_abs = ::Rails.root.join(config_path)
23
+ key_path_abs = ::Rails.root.join(key_path)
24
+
25
+ # We always want to use `config/credentials/[environment]` for consistency
26
+ # If the master credentials do not exist, and a user has not specificed an environment, default to development
27
+ if config_path == 'config/credentials.yml.enc' && ::File.exist?(config_path_abs.to_s) == false
28
+ config_path_abs = ::Rails.root.join('config/credentials/development.yml.enc')
29
+ key_path_abs = ::Rails.root.join('config/credentials/development.key')
30
+ end
31
+
32
+ [
33
+ config_path_abs,
34
+ key_path_abs,
35
+ ]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'fileutils'
3
4
  require 'pathname'
4
5
  require 'tmpdir'
5
6
 
@@ -8,6 +9,8 @@ require 'active_support/core_ext/hash'
8
9
  require 'active_support/core_ext/module/delegation'
9
10
  require 'active_support/core_ext/object/inclusion'
10
11
 
12
+ require 'diffcrypt/file'
13
+
11
14
  module Diffcrypt
12
15
  module Rails
13
16
  class EncryptedConfiguration
@@ -20,13 +23,13 @@ module Diffcrypt
20
23
  delegate_missing_to :options
21
24
 
22
25
  def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
23
- @content_path = Pathname.new(File.absolute_path(config_path)).yield_self do |path|
26
+ @content_path = Pathname.new(::File.absolute_path(config_path)).yield_self do |path|
24
27
  path.symlink? ? path.realpath : path
25
28
  end
29
+ @diffcrypt_file = Diffcrypt::File.new(@content_path)
26
30
  @key_path = Pathname.new(key_path)
27
31
  @env_key = env_key
28
32
  @raise_if_missing_key = raise_if_missing_key
29
- @active_support_encryptor = ActiveSupport::MessageEncryptor.new([key].pack('H*'), cipher: Encryptor::CIPHER)
30
33
  end
31
34
 
32
35
  # Determines if file is using the diffable format, or still
@@ -50,7 +53,7 @@ module Diffcrypt
50
53
  deserialize(contents)
51
54
 
52
55
  IO.binwrite "#{content_path}.tmp", encrypt(contents, original_encrypted_contents)
53
- FileUtils.mv "#{content_path}.tmp", content_path
56
+ ::FileUtils.mv "#{content_path}.tmp", content_path
54
57
  end
55
58
 
56
59
  def config
@@ -72,7 +75,7 @@ module Diffcrypt
72
75
  # rubocop:disable Metrics/AbcSize
73
76
  def writing(contents)
74
77
  tmp_file = "#{Process.pid}.#{content_path.basename.to_s.chomp('.enc')}"
75
- tmp_path = Pathname.new File.join(Dir.tmpdir, tmp_file)
78
+ tmp_path = Pathname.new ::File.join(Dir.tmpdir, tmp_file)
76
79
  tmp_path.binwrite contents
77
80
 
78
81
  yield tmp_path
@@ -81,10 +84,17 @@ module Diffcrypt
81
84
 
82
85
  write(updated_contents, content_path_diffable? && content_path.binread)
83
86
  ensure
84
- FileUtils.rm(tmp_path) if tmp_path&.exist?
87
+ ::FileUtils.rm(tmp_path) if tmp_path&.exist?
85
88
  end
86
89
  # rubocop:enable Metrics/AbcSize
87
90
 
91
+ # Standard rails credentials encrypt the entire file. We need to detect this to use the correct
92
+ # data interface
93
+ # @return [Boolean]
94
+ def rails_native_credentials?(contents)
95
+ contents.index('---').nil?
96
+ end
97
+
88
98
  # @param [String] contents The new content to be encrypted
89
99
  # @param [String] diff_against The original (encrypted) content to determine which keys have changed
90
100
  # @return [String] Encrypted content to commit
@@ -95,16 +105,25 @@ module Diffcrypt
95
105
  # @param [String] contents
96
106
  # @return [String]
97
107
  def decrypt(contents)
98
- if contents.index('---').nil?
99
- @active_support_encryptor.decrypt_and_verify contents
108
+ if rails_native_credentials?(contents)
109
+ active_support_encryptor.decrypt_and_verify contents
100
110
  else
101
111
  encryptor.decrypt contents
102
112
  end
103
113
  end
104
114
 
115
+ # Rails applications with an existing credentials file, the inbuilt active support encryptor should be used
116
+ # @return [ActiveSupport::MessageEncryptor]
117
+ def active_support_encryptor
118
+ @active_support_encryptor ||= ActiveSupport::MessageEncryptor.new(
119
+ [key].pack('H*'),
120
+ cipher: 'aes-128-gcm',
121
+ )
122
+ end
123
+
105
124
  # @return [Encryptor]
106
125
  def encryptor
107
- @encryptor ||= Encryptor.new key
126
+ @encryptor ||= Encryptor.new key, cipher: @diffcrypt_file.cipher
108
127
  end
109
128
 
110
129
  def read_env_key
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'diffcrypt/rails/application_helper'
4
+
5
+ module Diffcrypt
6
+ class Railtie < ::Rails::Railtie
7
+ railtie_name :diffcrypt
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,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :diffcrypt do
4
+ desc 'Initialize credentials for all environments'
5
+ task :init, %i[environments] do |_t, args|
6
+ args.with_defaults(
7
+ environments: 'development,test,staging,production',
8
+ )
9
+ environments = args.environments.split(',')
10
+
11
+ environments.each do |environment|
12
+ key_path = Rails.root.join('config', 'credentials', "#{environment}.key")
13
+ file_path = Rails.root.join('config', 'credentials', "#{environment}.yml.enc")
14
+ gitignore_path = Rails.root.join('.gitignore')
15
+ next if File.exist?(file_path) || File.exist?(key_path)
16
+
17
+ # Generate a new key
18
+ key = Diffcrypt::Encryptor.generate_key
19
+ key_dir = File.dirname(key_path)
20
+ Dir.mkdir(key_dir) unless Dir.exist?(key_dir)
21
+ ::File.write(key_path, key)
22
+
23
+ # Encrypt default contents
24
+ file = Diffcrypt::File.new(file_path)
25
+ data = {
26
+ 'secret_key_base' => SecureRandom.hex(32),
27
+ }
28
+ file.write(key, data)
29
+
30
+ # Ensure .key files are always ignored
31
+ ::File.open(gitignore_path, 'a') do |f|
32
+ f.write("\nconfig/credentials/*.key")
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Diffcrypt
4
- VERSION = '0.3.1'
4
+ VERSION = '0.5.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: diffcrypt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Qualie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-07-08 00:00:00.000000000 Z
11
+ date: 2020-12-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -62,6 +62,7 @@ files:
62
62
  - LICENSE.txt
63
63
  - README.md
64
64
  - Rakefile
65
+ - SECURITY.md
65
66
  - bin/console
66
67
  - bin/diffcrypt
67
68
  - bin/setup
@@ -69,8 +70,13 @@ files:
69
70
  - lib/diffcrypt.rb
70
71
  - lib/diffcrypt/cli.rb
71
72
  - lib/diffcrypt/encryptor.rb
73
+ - lib/diffcrypt/file.rb
74
+ - lib/diffcrypt/rails/application_helper.rb
72
75
  - lib/diffcrypt/rails/encrypted_configuration.rb
76
+ - lib/diffcrypt/railtie.rb
77
+ - lib/diffcrypt/tasks/rails.rake
73
78
  - lib/diffcrypt/version.rb
79
+ - tmp/.keep
74
80
  homepage: https://github.com/marcqualie/diffcrypt
75
81
  licenses:
76
82
  - MIT
@@ -85,7 +91,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
91
  requirements:
86
92
  - - ">="
87
93
  - !ruby/object:Gem::Version
88
- version: 2.3.0
94
+ version: 2.6.0
89
95
  required_rubygems_version: !ruby/object:Gem::Requirement
90
96
  requirements:
91
97
  - - ">="