encrypt_env 1.1.5 → 1.2.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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/bin/encrypt_env +11 -3
  3. data/lib/encrypt_env.rb +151 -77
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16d031a8da5dcdc509f7248f688d4f9741273a042478210c3840a8e9effa25ce
4
- data.tar.gz: 59f6115add313d8989dda0a31c0c4427bda3ccb5b5f0890eeb3ea01d188da46e
3
+ metadata.gz: 31ee9ab6595f4b030378dbab20064c2a6fbaf78703ceb8d673b6a937d21c5ab0
4
+ data.tar.gz: b4e53740a417b02fe1b4920609f11b9fedb9d1913b70ab7c0593f67b3599a994
5
5
  SHA512:
6
- metadata.gz: db4b7762c832b2d5227faec889ba424b9319d670dd7f99ba2bcdd78421bd438627e06c70dda68005f275e35b23e80489995572ce1a13b23cef3815c926bcfbac
7
- data.tar.gz: '098cdbf938ce9e556eae215f81fc92e5986bbc35f40ae855075ef7ddf1e95acfdee53fe743f75a98902e6b1d77ad0b34041a45d9451fb6c1833fa0577877cc32'
6
+ metadata.gz: de90b6a660b19c26bde561133ac8816c06c3b87bb8d3cda02c3ba8cafaf3063e5b4937b50376de233da2a060339cc92a04e4ff8af811e23794a31daf5d75af4d
7
+ data.tar.gz: 2515b17576ab29ff0a71414c100e23e974029c7c4dccc04c43def34eb761e06cdbbeb7fdac0bd912950b7f7c4b9c4b8c12b0ea690f3459d294f51b070ba9fa0e
data/bin/encrypt_env CHANGED
@@ -11,13 +11,21 @@ if action == 'setup'
11
11
  EncryptEnv.setup
12
12
  exit 0
13
13
  elsif action == 'show'
14
- EncryptEnv.show
14
+ if argv[0]
15
+ EncryptEnv.show(argv[0])
16
+ else
17
+ EncryptEnv.show
18
+ end
15
19
  exit 0
16
20
  elsif action == 'all'
17
- EncryptEnv.show_all
21
+ EncryptEnv.show('all')
18
22
  exit 0
19
23
  elsif action == 'edit'
20
- EncryptEnv.edit
24
+ if argv[0]
25
+ EncryptEnv.edit(argv[0])
26
+ else
27
+ EncryptEnv.edit
28
+ end
21
29
  exit 0
22
30
  elsif ['help', '--help', '-h'].include?(action)
23
31
  puts <<~HELP
data/lib/encrypt_env.rb CHANGED
@@ -8,126 +8,200 @@ require 'tempfile'
8
8
  require 'json'
9
9
 
10
10
  # gem 'encrypt_env'
11
+ # rubocop:disable Metrics/ClassLength
12
+ # rubocop:disable Metrics/MethodLength
11
13
  class EncryptEnv
12
- private_class_method def self.master_key
13
- key = File.read("#{@path_root}/config/master.key").strip
14
- [key].pack('H*')
14
+ private_class_method def self.define_option
15
+ puts "Options to 'encrypt secrets.yml' file"
16
+ puts '1. Generate only one master.key and one encrypted file for all environment'
17
+ puts '2. Generate master.key and encrypted file for each environment'
18
+
19
+ loop do
20
+ @opt = gets.chomp.to_i
21
+ break if @opt == 1 || @opt == 2
22
+
23
+ puts "Please enter '1' or '2'!"
24
+ end
25
+
26
+ puts "Your option is #{@opt}"
27
+ end
28
+
29
+ private_class_method def self.load_curr_opt
30
+ if File.file?("#{Dir.pwd}/config/secrets.yml.enc")
31
+ @opt = 1
32
+ elsif Dir["#{Dir.pwd}/config/secrets_*.yml.enc"].length.positive?
33
+ @opt = 2
34
+ else
35
+ puts 'You must setup first to encrypt file!'
36
+ exit
37
+ end
38
+ end
39
+
40
+ private_class_method def self.current_env
41
+ unless defined?(Rails)
42
+ env = `rails r "print Rails.env"`
43
+ return env
44
+ end
45
+ Rails.env
46
+ end
47
+
48
+ private_class_method def self.check_key_existence(env = nil)
49
+ file_name = env.nil? ? 'master.key' : "master_#{env}.key"
50
+ return if File.file?("#{Dir.pwd}/config/#{file_name}")
51
+ # return if Dir["#{Dir.pwd}/config/master_*.key"].length.positive? && @opt == 2
52
+ return if ENV.key?('MASTER_KEY')
53
+
54
+ puts 'Please provide master key!'
55
+ exit
56
+ end
57
+
58
+ private_class_method def self.load_master_key(env = nil)
59
+ check_key_existence(env)
60
+ file_path = env ? "#{Dir.pwd}/config/master_#{env}.key" : "#{Dir.pwd}/config/master.key"
61
+ key = File.file?(file_path) ? File.read(file_path).strip : ENV['MASTER_KEY']
62
+ @master_key = [key].pack('H*')
15
63
  end
16
64
 
17
- private_class_method def self.data_decrypt(raw_data)
65
+ private_class_method def self.generate_keys
66
+ if @opt == 1
67
+ key = OpenSSL::Random.random_bytes(16)
68
+ File.open("#{Dir.pwd}/config/master.key", 'w') { |file| file.write(key.unpack('H*')[0]) }
69
+ else
70
+ to_hash_type(@content_to_encrypt).each_key do |env|
71
+ next if env == 'default'
72
+
73
+ key = OpenSSL::Random.random_bytes(16)
74
+ File.open("#{Dir.pwd}/config/master_#{env}.key", 'w') { |file| file.write(key.unpack('H*')[0]) }
75
+ end
76
+ end
77
+ end
78
+
79
+ private_class_method def self.load_content_to_encrypt
80
+ secret_file = File.expand_path("#{Dir.pwd}/config/secrets.yml")
81
+ @content_to_encrypt = File.read(secret_file)
82
+ end
83
+
84
+ private_class_method def self.to_hash_type(raw_data)
85
+ HashWithIndifferentAccess.new(YAML.load(raw_data, aliases: true))
86
+ end
87
+
88
+ private_class_method def self.load_encrypted_data(env = nil)
89
+ file_path = env ? "#{Dir.pwd}/config/secrets_#{env}.yml.enc" : "#{Dir.pwd}/config/secrets.yml.enc"
90
+ hex_string = File.read(file_path)
91
+ raw_data = [hex_string].pack('H*')
92
+
18
93
  encrypted = raw_data.slice(0, raw_data.length - 28)
19
94
  iv = raw_data.slice(raw_data.length - 28, 12)
20
95
  tag = raw_data.slice(raw_data.length - 16, 16)
21
96
  { encrypted: encrypted, iv: iv, tag: tag }
22
97
  end
23
98
 
24
- private_class_method def self.encrypt(content)
99
+ private_class_method def self.encrypt(content, typ = nil)
100
+ file_path = typ ? "#{Dir.pwd}/config/secrets_#{typ}.yml.enc" : "#{Dir.pwd}/config/secrets.yml.enc"
25
101
  cipher = OpenSSL::Cipher.new('aes-128-gcm')
26
102
  cipher.encrypt
27
- cipher.key = master_key
103
+ cipher.key = @master_key
28
104
  iv = cipher.random_iv
29
105
  cipher.auth_data = ''
30
106
  encrypted = cipher.update(content) + cipher.final
31
107
  tag = cipher.auth_tag
32
108
  hex_string = (encrypted + iv + tag).unpack('H*')[0]
33
- File.open("#{@path_root}/config/secrets.yml.enc", 'w') { |file| file.write(hex_string) }
109
+ File.open(file_path, 'w') { |file| file.write(hex_string) }
34
110
  end
35
111
 
36
- private_class_method def self.decrypt
112
+ private_class_method def self.decrypt(env = nil)
113
+ load_master_key(env)
114
+
37
115
  decipher = OpenSSL::Cipher.new('aes-128-gcm')
38
116
  decipher.decrypt
39
- hex_string = File.read("#{@path_root}/config/secrets.yml.enc")
40
- data = data_decrypt([hex_string].pack('H*'))
117
+ data = load_encrypted_data(env)
41
118
  encrypted = data[:encrypted]
42
- decipher.key = master_key
119
+ decipher.key = @master_key
43
120
  decipher.iv = data[:iv]
44
121
  decipher.auth_tag = data[:tag]
45
122
  decipher.auth_data = ''
46
123
 
47
- decipher.update(encrypted) + decipher.final
124
+ @raw_decrypted = decipher.update(encrypted) + decipher.final
125
+ @decrypted = to_hash_type(@raw_decrypted)
126
+ # Catch error if master key is wrong
127
+ rescue OpenSSL::Cipher::CipherError
128
+ puts 'Master key is wrong!'
129
+ exit
48
130
  end
49
131
 
50
- private_class_method def self.path_root
51
- @path_root = if defined?(Rails)
52
- Rails.root.to_s
53
- elsif defined?(Bundler)
54
- Bundler.root.to_s
55
- else
56
- Dir.pwd
57
- end
58
- end
59
-
60
- def self.setup
61
- path_root
62
- @secret_file = File.expand_path("#{@path_root}/config/secrets.yml")
63
- key = OpenSSL::Random.random_bytes(16)
64
- # save key in master.key file
65
- File.open("#{@path_root}/config/master.key", 'w') { |file| file.write(key.unpack('H*')[0]) }
66
- encrypt(File.read(@secret_file))
67
- File.rename(@secret_file, "#{@path_root}/config/secrets.yml.old")
68
- system("echo '/config/master.key' >> #{@path_root}/.gitignore")
69
- system("echo '/config/secrets.yml.old' >> #{@path_root}/.gitignore")
70
- system("echo 'Set up complete!'")
71
- end
72
-
73
- def self.edit
74
- path_root unless @path_root
75
- secrets unless @decrypted
76
- Tempfile.create('secrets.yml') do |f|
77
- f.write(decrypt)
78
- f.flush
79
- f.rewind
80
- system("vim #{f.path}")
81
- encrypt(File.read(f.path))
82
- @decrypted = nil
132
+ private_class_method def self.all_decrypted_object
133
+ obj = {}
134
+ env_lst = Dir["#{Dir.pwd}/config/secrets_*.yml.enc"].map do |path|
135
+ path.scan(/secrets_(.*)\.yml\.enc/).flatten.first
136
+ end
137
+ env_lst.each do |e|
138
+ decrypt(e)
139
+ obj[e] = @decrypted
83
140
  end
141
+ obj
84
142
  end
85
143
 
86
144
  def self.secrets_all
87
- path_root unless @path_root
88
- secrets unless @decrypted
145
+ return all_decrypted_object if @opt == 2
146
+
147
+ decrypt
89
148
  @decrypted
90
149
  end
91
150
 
92
- def self.secrets
93
- @decrypted
151
+ def self.secrets(env = nil)
152
+ load_curr_opt unless @opt
153
+ return secrets_all if env == 'all'
94
154
 
95
- path_root unless @path_root
96
- @decrypted = HashWithIndifferentAccess.new(YAML.load(decrypt, aliases: true))
97
- unless defined?(Rails)
98
- env = `rails r "print Rails.env"`.to_sym
99
- return @decrypted[env] || @decrypted[:default] || @decrypted
155
+ if @opt == 1
156
+ decrypt
157
+ @decrypted[env || current_env]
158
+ else
159
+ decrypt(env || current_env)
160
+ @decrypted
100
161
  end
101
- @decrypted[Rails.env.to_sym] || @decrypted[:default] || @decrypted
102
162
  end
103
163
 
104
- def self.secrets_production
105
- secrets unless @decrypted
106
- @decrypted[:production]
107
- end
108
-
109
- def self.secrets_development
110
- secrets unless @decrypted
111
- @decrypted[:development]
112
- end
164
+ def self.setup
165
+ define_option
166
+ load_content_to_encrypt
167
+ generate_keys
168
+
169
+ if @opt == 1
170
+ load_master_key
171
+ encrypt(@content_to_encrypt)
172
+ else
173
+ to_hash_type(@content_to_encrypt).each do |env, value|
174
+ next if env == 'default'
175
+
176
+ load_master_key(env)
177
+ encrypt(value.to_hash.to_yaml, env)
178
+ end
179
+ end
113
180
 
114
- def self.secrets_test
115
- secrets unless @decrypted
116
- @decrypted[:test]
181
+ File.rename("#{Dir.pwd}/config/secrets.yml", "#{Dir.pwd}/config/secrets.yml.old")
182
+ system("echo '/config/master*.key' >> #{Dir.pwd}/.gitignore")
183
+ system("echo '/config/secrets.yml.old' >> #{Dir.pwd}/.gitignore")
184
+ system("echo 'Set up complete!'")
117
185
  end
118
186
 
119
- def self.secrets_staging
120
- secrets unless @decrypted
121
- @decrypted[:staging]
122
- end
187
+ def self.edit(env = nil)
188
+ load_curr_opt unless @opt
189
+ env ||= current_env if @opt == 2
190
+ return unless decrypt(env)
123
191
 
124
- def self.show
125
- # Pry::ColorPrinter.pp(secrets)
126
- jj secrets
192
+ Tempfile.create("secrets_#{env}.yml") do |f|
193
+ f.write(@raw_decrypted)
194
+ f.flush
195
+ f.rewind
196
+ system("vim #{f.path}")
197
+ encrypt(File.read(f.path), env)
198
+ @decrypted = nil
199
+ end
127
200
  end
128
201
 
129
- def self.show_all
130
- # Pry::ColorPrinter.pp(secrets_all)
131
- jj secrets_all
202
+ def self.show(env = nil)
203
+ jj secrets(env)
132
204
  end
133
205
  end
206
+ # rubocop:enable Metrics/ClassLength
207
+ # rubocop:enable Metrics/MethodLength
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: encrypt_env
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.5
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nhu Tan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-05 00:00:00.000000000 Z
11
+ date: 2022-08-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Encrypts and decrypts environment variables
14
14
  email: nhutan2001@gmail.com
@@ -39,7 +39,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  requirements: []
42
- rubygems_version: 3.0.3.1
42
+ rubygems_version: 3.3.7
43
43
  signing_key:
44
44
  specification_version: 4
45
45
  summary: Ecrypt secrets.yml file