sym 2.8.0 → 2.10.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 +5 -5
- data/.circleci/config.yml +30 -31
- data/.envrc +7 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +150 -928
- data/.travis.yml +16 -26
- data/CHANGELOG.md +206 -167
- data/Gemfile +1 -0
- data/README.adoc +650 -0
- data/Rakefile +9 -3
- data/bin/{sym.completion → sym.completion.bash} +9 -14
- data/bin/sym.symit.bash +781 -0
- data/codecov.yml +29 -0
- data/design/sym-help.png +0 -0
- data/exe/keychain +1 -1
- data/exe/sym +5 -2
- data/lib/ruby_warnings.rb +7 -0
- data/lib/sym.rb +1 -7
- data/lib/sym/app.rb +1 -1
- data/lib/sym/app/args.rb +3 -2
- data/lib/sym/app/cli.rb +1 -2
- data/lib/sym/app/cli_slop.rb +1 -1
- data/lib/sym/app/commands.rb +1 -1
- data/lib/sym/app/commands/base_command.rb +1 -1
- data/lib/sym/app/commands/bash_completion.rb +20 -8
- data/lib/sym/app/commands/open_editor.rb +1 -1
- data/lib/sym/app/commands/password_protect_key.rb +4 -4
- data/lib/sym/app/commands/show_examples.rb +1 -1
- data/lib/sym/app/input/handler.rb +7 -1
- data/lib/sym/app/keychain.rb +15 -9
- data/lib/sym/app/output/noop.rb +2 -1
- data/lib/sym/app/password/cache.rb +1 -1
- data/lib/sym/app/password/providers.rb +2 -3
- data/lib/sym/app/private_key/decryptor.rb +2 -2
- data/lib/sym/app/private_key/detector.rb +4 -7
- data/lib/sym/application.rb +6 -11
- data/lib/sym/constants.rb +28 -13
- data/lib/sym/data/wrapper_struct.rb +20 -12
- data/lib/sym/errors.rb +11 -2
- data/lib/sym/extensions/instance_methods.rb +7 -8
- data/lib/sym/extensions/stdlib.rb +0 -1
- data/lib/sym/extensions/with_retry.rb +1 -1
- data/lib/sym/extensions/with_timeout.rb +1 -1
- data/lib/sym/version.rb +30 -5
- data/sym.gemspec +35 -35
- metadata +88 -71
- data/.codeclimate.yml +0 -30
- data/README.md +0 -623
- data/bin/sym.symit +0 -565
- data/lib/sym/app/password/providers/drb_provider.rb +0 -41
data/codecov.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
codecov:
|
2
|
+
require_ci_to_pass: no
|
3
|
+
|
4
|
+
notify:
|
5
|
+
wait_for_ci: yes
|
6
|
+
|
7
|
+
parsers:
|
8
|
+
v1:
|
9
|
+
include_full_missed_files: true # To use with Ruby so we see files that have NO tests written
|
10
|
+
|
11
|
+
coverage:
|
12
|
+
range: 50..75
|
13
|
+
round: down
|
14
|
+
precision: 1
|
15
|
+
status:
|
16
|
+
project:
|
17
|
+
default: off
|
18
|
+
sym:
|
19
|
+
target: 70%
|
20
|
+
threshold: 10%
|
21
|
+
informational: true
|
22
|
+
if_not_found: success
|
23
|
+
if_ci_failed: error
|
24
|
+
paths:
|
25
|
+
- lib/
|
26
|
+
flags:
|
27
|
+
sym:
|
28
|
+
paths:
|
29
|
+
- lib/
|
data/design/sym-help.png
CHANGED
Binary file
|
data/exe/keychain
CHANGED
data/exe/sym
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# vim: ft=ruby
|
3
|
+
|
4
|
+
require_relative '../lib/ruby_warnings'
|
2
5
|
|
3
6
|
lib_path = File.expand_path(File.dirname(__FILE__) + '/../lib')
|
4
7
|
$LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path)
|
@@ -6,12 +9,12 @@ $LOAD_PATH << lib_path if File.exist?(lib_path) && !$LOAD_PATH.include?(lib_path
|
|
6
9
|
require 'sym'
|
7
10
|
require 'sym/app'
|
8
11
|
|
9
|
-
#ARGV.any?{ |a| a =~ /^-/ } ?
|
12
|
+
# ARGV.any?{ |a| a =~ /^-/ } ?
|
10
13
|
begin
|
11
14
|
exit ::Sym::App::CLI.new(ARGV.dup).execute
|
12
15
|
rescue Interrupt => e
|
13
16
|
STDERR.flush
|
14
|
-
|
17
|
+
warn "Interrupt, #{e.message}, exiting."
|
15
18
|
STDERR.flush
|
16
19
|
exit 1
|
17
20
|
end
|
data/lib/sym.rb
CHANGED
@@ -21,17 +21,11 @@ Sym::Configuration.configure do |config|
|
|
21
21
|
# When nil is selected, providers are auto-detected.
|
22
22
|
config.password_cache_default_provider = nil
|
23
23
|
config.password_cache_arguments = {
|
24
|
-
drb: {
|
25
|
-
opts: {
|
26
|
-
uri: 'druby://127.0.0.1:24924'
|
27
|
-
}
|
28
|
-
},
|
29
24
|
memcached: {
|
30
25
|
args: %w(127.0.0.1:11211),
|
31
26
|
opts: { namespace: 'sym',
|
32
27
|
compress: true,
|
33
|
-
expires_in: config.password_cache_timeout
|
34
|
-
}
|
28
|
+
expires_in: config.password_cache_timeout}
|
35
29
|
|
36
30
|
}
|
37
31
|
}
|
data/lib/sym/app.rb
CHANGED
data/lib/sym/app/args.rb
CHANGED
@@ -12,7 +12,7 @@ module Sym
|
|
12
12
|
|
13
13
|
def initialize(opts)
|
14
14
|
self.opts = opts
|
15
|
-
self.selected_options = opts.keys.
|
15
|
+
self.selected_options = opts.keys.select { |k| opts[k] }
|
16
16
|
end
|
17
17
|
|
18
18
|
def specify_key?
|
@@ -33,10 +33,11 @@ module Sym
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def provided_options
|
36
|
-
opts.to_hash.keys.
|
36
|
+
opts.to_hash.keys.select { |k| opts[k] }
|
37
37
|
end
|
38
38
|
|
39
39
|
private
|
40
|
+
|
40
41
|
def do?(list)
|
41
42
|
!(list & selected_options).empty?
|
42
43
|
end
|
data/lib/sym/app/cli.rb
CHANGED
@@ -60,7 +60,6 @@ module Sym
|
|
60
60
|
|
61
61
|
|
62
62
|
def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = nil)
|
63
|
-
|
64
63
|
self.stdin = stdin
|
65
64
|
self.stdout = stdout
|
66
65
|
self.stderr = stderr
|
@@ -140,7 +139,7 @@ module Sym
|
|
140
139
|
private
|
141
140
|
|
142
141
|
def log(*args)
|
143
|
-
Sym::App.log(*args, **
|
142
|
+
Sym::App.log(*args, **opts.to_hash)
|
144
143
|
end
|
145
144
|
|
146
145
|
def error(hash)
|
data/lib/sym/app/cli_slop.rb
CHANGED
@@ -55,7 +55,7 @@ module Sym
|
|
55
55
|
o.separator 'Create a new private key:'.yellow
|
56
56
|
o.bool '-g', '--generate', ' generate a new private key'
|
57
57
|
o.bool '-p', '--password', ' encrypt the key with a password'
|
58
|
-
if Sym::App.
|
58
|
+
if Sym::App.osx?
|
59
59
|
o.string '-x', '--keychain', '[key-name] '.blue + 'write the key to OS-X Keychain'
|
60
60
|
end
|
61
61
|
|
data/lib/sym/app/commands.rb
CHANGED
@@ -37,7 +37,7 @@ module Sym
|
|
37
37
|
# Sort commands based on the #dependencies array, which itself is sorted
|
38
38
|
# based on command dependencies.
|
39
39
|
def sorted_commands
|
40
|
-
@sorted_commands ||= self.commands.to_a.sort_by{|klass| dependencies.index(klass.short_name) }
|
40
|
+
@sorted_commands ||= self.commands.to_a.sort_by{ |klass| dependencies.index(klass.short_name) }
|
41
41
|
@sorted_commands
|
42
42
|
end
|
43
43
|
|
@@ -8,21 +8,24 @@ module Sym
|
|
8
8
|
try_after :generate_key, :open_editor, :encrypt, :decrypt
|
9
9
|
|
10
10
|
def ok
|
11
|
-
'
|
11
|
+
' '.bold.green
|
12
12
|
end
|
13
13
|
|
14
14
|
def execute
|
15
15
|
file = opts[:bash_support]
|
16
16
|
|
17
17
|
out = ''
|
18
|
-
Sym::Constants::Bash::
|
18
|
+
Sym::Constants::Bash::CONFIG.each_pair do |key, config|
|
19
19
|
script_name = key.to_s
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
|
21
|
+
# This removes the old version of this file.
|
22
|
+
remove_old_version(out, config[:dest])
|
23
|
+
|
24
|
+
if File.exist?(config[:dest]) && File.read(config[:source]) == File.read(config[:dest])
|
25
25
|
out << "#{ok} file #{config[:dest].bold.blue } exists, and is up to date.\n"
|
26
|
+
else
|
27
|
+
FileUtils.cp(config[:source], config[:dest])
|
28
|
+
out << "#{ok} installing #{config[:dest].bold.blue }...\n"
|
26
29
|
end
|
27
30
|
|
28
31
|
out << if File.exist?(file)
|
@@ -40,7 +43,7 @@ module Sym
|
|
40
43
|
out << "\nPlease reload your terminal session to activate bash completion\n"
|
41
44
|
out << "and other installed BASH utilities.\n"
|
42
45
|
out << "\nAlternatively, just type #{"source #{file}".bold.green} to reload BASH.\n"
|
43
|
-
out << "Also — go ahead and try running #{
|
46
|
+
out << "Also — go ahead and try running #{'sym -h'.bold.blue} and #{'symit -h'.bold.blue}.\n"
|
44
47
|
end
|
45
48
|
|
46
49
|
private
|
@@ -51,6 +54,15 @@ module Sym
|
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
57
|
+
def remove_old_version(out, file)
|
58
|
+
if file =~ /\.bash$/
|
59
|
+
old_file = file.gsub(/\.bash$/, '')
|
60
|
+
if File.exist?(old_file)
|
61
|
+
out << "Removing old version — #{old_file.bold.magenta}..."
|
62
|
+
FileUtils.rm_f old_file
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
54
66
|
end
|
55
67
|
end
|
56
68
|
end
|
@@ -48,7 +48,7 @@ module Sym
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def timestamp
|
51
|
-
@timestamp ||= Time.now.to_a.select { |d| d.is_a?(
|
51
|
+
@timestamp ||= Time.now.to_a.select { |d| d.is_a?(Integer) }.map { |d| '%02d' % d }[0..-3].reverse.join
|
52
52
|
end
|
53
53
|
|
54
54
|
def process(code)
|
@@ -16,10 +16,10 @@ module Sym
|
|
16
16
|
the_key = self.key
|
17
17
|
|
18
18
|
if opts[:password]
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
encrypted_key, password = encrypt_with_password(the_key)
|
20
|
+
add_password_to_the_cache(encrypted_key, password)
|
21
|
+
the_key = encrypted_key
|
22
|
+
end
|
23
23
|
|
24
24
|
add_to_keychain_if_needed(the_key)
|
25
25
|
|
@@ -56,7 +56,7 @@ Diff:
|
|
56
56
|
# (c) 2016 Konstantin Gredeskoul. All rights reserved.'.green.bold)
|
57
57
|
|
58
58
|
|
59
|
-
if Sym::App.
|
59
|
+
if Sym::App.osx?
|
60
60
|
output << example(comment: 'generate a new password-encrypted key, save it to your Keychain:',
|
61
61
|
command: 'sym -gpcx staging.key')
|
62
62
|
|
@@ -27,7 +27,13 @@ module Sym
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def prompt(message, color)
|
30
|
-
|
30
|
+
unless STDIN.isatty && STDIN.tty?
|
31
|
+
raise Sym::Errors::CantReadPasswordNoTTY.new('key requires a password, however STDIN is not a TTY')
|
32
|
+
end
|
33
|
+
highline(message, color)
|
34
|
+
end
|
35
|
+
|
36
|
+
def highline(message, color)
|
31
37
|
HighLine.new(stdin, stderr).ask(message.bold) { |q| q.echo = '•'.send(color) }
|
32
38
|
end
|
33
39
|
|
data/lib/sym/app/keychain.rb
CHANGED
@@ -39,11 +39,13 @@ module Sym
|
|
39
39
|
self.key_name = key_name
|
40
40
|
self.opts = opts
|
41
41
|
self.class.validate!
|
42
|
-
stderr_off
|
42
|
+
opts[:trace] ? stderr_on : stderr_off
|
43
43
|
end
|
44
44
|
|
45
45
|
def add(password)
|
46
|
-
|
46
|
+
delete rescue nil
|
47
|
+
sleep 0.1
|
48
|
+
execute command(:add, " -T /usr/bin/security -w '#{password}' ")
|
47
49
|
end
|
48
50
|
|
49
51
|
def find
|
@@ -56,10 +58,14 @@ module Sym
|
|
56
58
|
|
57
59
|
def execute(command)
|
58
60
|
command += ' 2>/dev/null' if stderr_disabled
|
59
|
-
puts "> #{command.yellow
|
61
|
+
puts "> #{command.yellow}" if opts[:verbose]
|
60
62
|
output = `#{command}`
|
61
63
|
result = $?
|
62
|
-
|
64
|
+
unless result.success?
|
65
|
+
warn "> ERROR running command:\n> $ #{output.red}" if !stderr_disabled && opts[:verbose]
|
66
|
+
raise Sym::Errors::KeyChainCommandError.new("Command error: #{result}, command: #{command}")
|
67
|
+
end
|
68
|
+
|
63
69
|
output.chomp
|
64
70
|
rescue Errno::ENOENT => e
|
65
71
|
raise Sym::Errors::KeyChainCommandError.new("Command error: #{e.message}, command: #{command}")
|
@@ -80,16 +86,16 @@ module Sym
|
|
80
86
|
out << extras if extras
|
81
87
|
out = out.join
|
82
88
|
# Do not actually ever run these commands on non MacOSX
|
83
|
-
out = "echo Run this –\"#{out}\", on #{Sym::App.this_os}?\nAre you sure?" unless Sym::App.
|
89
|
+
out = "echo Run this –\"#{out}\", on #{Sym::App.this_os}?\nAre you sure?" unless Sym::App.osx?
|
84
90
|
out
|
85
91
|
end
|
86
92
|
|
87
93
|
def base_command(action)
|
88
94
|
[
|
89
|
-
"security #{action}-#{self.class.sub_section} ",
|
90
|
-
"-a
|
91
|
-
"-D
|
92
|
-
"-s
|
95
|
+
"/usr/bin/security #{action}-#{self.class.sub_section} ",
|
96
|
+
"-a #{self.class.user} ",
|
97
|
+
"-D #{self.class.kind} ",
|
98
|
+
"-s #{self.key_name} "
|
93
99
|
]
|
94
100
|
end
|
95
101
|
end
|
data/lib/sym/app/output/noop.rb
CHANGED
@@ -36,7 +36,7 @@ module Sym
|
|
36
36
|
self.enabled = opts[:enabled]
|
37
37
|
self.verbose = opts[:verbose]
|
38
38
|
self.timeout = opts[:timeout] || ::Sym::Configuration.config.password_cache_timeout
|
39
|
-
self.provider = Providers.provider(opts[:provider], opts[:provider_opts] || {})
|
39
|
+
self.provider = Providers.provider(opts[:provider], **(opts[:provider_opts] || {}))
|
40
40
|
self.enabled = false unless self.provider
|
41
41
|
self
|
42
42
|
end
|
@@ -15,7 +15,7 @@ module Sym
|
|
15
15
|
self.providers << provider_class
|
16
16
|
end
|
17
17
|
|
18
|
-
# Detect first instance
|
18
|
+
# Detect first instance tht is "alive?" and return it.
|
19
19
|
def detect
|
20
20
|
self.detected ||= self.providers.inject(nil) do |instance, provider_class|
|
21
21
|
instance || (p = provider_class.new; p.alive? ? p : nil)
|
@@ -38,7 +38,7 @@ module Sym
|
|
38
38
|
|
39
39
|
def provider_from_argument(p, **opts, &block)
|
40
40
|
case p
|
41
|
-
|
41
|
+
when String, Symbol
|
42
42
|
provider_class_name = "#{p.to_s.capitalize}Provider"
|
43
43
|
Sym::App::Password::Providers.const_defined?(provider_class_name) ?
|
44
44
|
Sym::App::Password::Providers.const_get(provider_class_name).new(**opts, &block) :
|
@@ -53,4 +53,3 @@ end
|
|
53
53
|
|
54
54
|
# Order is important — they are tried in this order for auto detect
|
55
55
|
require 'sym/app/password/providers/memcached_provider'
|
56
|
-
require 'sym/app/password/providers/drb_provider'
|
@@ -31,10 +31,10 @@ module Sym
|
|
31
31
|
rescue ::OpenSSL::Cipher::CipherError => e
|
32
32
|
input_handler.puts 'Invalid password. Please try again.'
|
33
33
|
|
34
|
-
if (
|
34
|
+
if (retries += 1) < 3
|
35
35
|
retry
|
36
36
|
else
|
37
|
-
raise(Sym::Errors::
|
37
|
+
raise(Sym::Errors::WrongPasswordForKey.new('Invalid password.'))
|
38
38
|
end
|
39
39
|
end
|
40
40
|
else
|
@@ -23,11 +23,10 @@ module Sym
|
|
23
23
|
# procs on a given string.
|
24
24
|
def read!
|
25
25
|
KeySourceCheck::CHECKS.each do |source_check|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
26
|
+
next unless result = source_check.detect(self) rescue nil
|
27
|
+
if key_ = normalize_key(result.key)
|
28
|
+
key_source_ = result.to_s
|
29
|
+
return key_, key_source_
|
31
30
|
end
|
32
31
|
end
|
33
32
|
nil
|
@@ -51,8 +50,6 @@ module Sym
|
|
51
50
|
rescue
|
52
51
|
nil
|
53
52
|
end
|
54
|
-
else
|
55
|
-
nil
|
56
53
|
end
|
57
54
|
end
|
58
55
|
end
|
data/lib/sym/application.rb
CHANGED
@@ -32,7 +32,6 @@ module Sym
|
|
32
32
|
:stdin, :stdout, :stderr, :kernel
|
33
33
|
|
34
34
|
def initialize(opts, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = nil)
|
35
|
-
|
36
35
|
self.stdin = stdin
|
37
36
|
self.stdout = stdout
|
38
37
|
self.stderr = stderr
|
@@ -111,16 +110,12 @@ module Sym
|
|
111
110
|
end
|
112
111
|
|
113
112
|
def editor
|
114
|
-
editors_to_try.find { |editor| File.exist?(editor) }
|
113
|
+
editors_to_try.compact.find { |editor| File.exist?(editor) }
|
115
114
|
end
|
116
115
|
|
117
116
|
def process_output(result)
|
118
|
-
unless result.is_a?(Hash)
|
119
|
-
|
120
|
-
result
|
121
|
-
else
|
122
|
-
result
|
123
|
-
end
|
117
|
+
self.output.call(result) unless result.is_a?(Hash)
|
118
|
+
result
|
124
119
|
end
|
125
120
|
|
126
121
|
private
|
@@ -182,7 +177,7 @@ module Sym
|
|
182
177
|
args[:verbose] = opts[:verbose]
|
183
178
|
args[:provider] = opts[:cache_provider] if opts[:cache_provider]
|
184
179
|
|
185
|
-
self.password_cache = Sym::App::Password::Cache.instance.configure(args)
|
180
|
+
self.password_cache = Sym::App::Password::Cache.instance.configure(**args)
|
186
181
|
end
|
187
182
|
|
188
183
|
def process_edit_option
|
@@ -207,7 +202,7 @@ module Sym
|
|
207
202
|
end
|
208
203
|
|
209
204
|
def initialize_action
|
210
|
-
self.action = if opts[:encrypt]
|
205
|
+
self.action = if opts[:encrypt]
|
211
206
|
:encr
|
212
207
|
elsif opts[:decrypt]
|
213
208
|
:decr
|
@@ -217,7 +212,7 @@ module Sym
|
|
217
212
|
# If we are encrypting or decrypting, and no data has been provided, check if we
|
218
213
|
# should read from STDIN
|
219
214
|
def initialize_data_source
|
220
|
-
if self.action && opts[:string].nil? && opts[:file].nil? && !
|
215
|
+
if self.action && opts[:string].nil? && opts[:file].nil? && !self.stdin.tty?
|
221
216
|
opts[:file] = '-'
|
222
217
|
end
|
223
218
|
end
|