sym 2.8.1 → 3.0.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 (55) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +31 -30
  3. data/.envrc +7 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +150 -928
  6. data/.travis.yml +16 -26
  7. data/CHANGELOG.md +220 -167
  8. data/Gemfile +1 -0
  9. data/LICENSE +2 -2
  10. data/README.adoc +670 -0
  11. data/Rakefile +10 -4
  12. data/bin/changelog +34 -0
  13. data/bin/sym.completion.bash +6 -4
  14. data/bin/sym.symit.bash +412 -187
  15. data/codecov.yml +29 -0
  16. data/design/sym-class-dependency-future-refactor.png +0 -0
  17. data/design/sym-class-dependency-vertical.png +0 -0
  18. data/design/sym-class-dependency.graffle +0 -0
  19. data/design/sym-class-dependency.png +0 -0
  20. data/design/sym-help.png +0 -0
  21. data/exe/keychain +1 -1
  22. data/exe/sym +5 -2
  23. data/lib/ruby_warnings.rb +7 -0
  24. data/lib/sym.rb +2 -8
  25. data/lib/sym/app.rb +1 -2
  26. data/lib/sym/app/args.rb +3 -2
  27. data/lib/sym/app/cli.rb +34 -21
  28. data/lib/sym/app/cli_slop.rb +9 -2
  29. data/lib/sym/app/commands.rb +1 -1
  30. data/lib/sym/app/commands/base_command.rb +1 -1
  31. data/lib/sym/app/commands/bash_completion.rb +2 -2
  32. data/lib/sym/app/commands/open_editor.rb +1 -1
  33. data/lib/sym/app/commands/password_protect_key.rb +4 -4
  34. data/lib/sym/app/commands/show_examples.rb +1 -1
  35. data/lib/sym/app/input/handler.rb +7 -1
  36. data/lib/sym/app/keychain.rb +15 -9
  37. data/lib/sym/app/output/noop.rb +2 -1
  38. data/lib/sym/app/password/cache.rb +1 -1
  39. data/lib/sym/app/password/providers.rb +2 -3
  40. data/lib/sym/app/private_key/decryptor.rb +2 -2
  41. data/lib/sym/app/private_key/detector.rb +4 -7
  42. data/lib/sym/application.rb +6 -11
  43. data/lib/sym/constants.rb +39 -23
  44. data/lib/sym/data/wrapper_struct.rb +20 -12
  45. data/lib/sym/errors.rb +13 -2
  46. data/lib/sym/extensions/instance_methods.rb +7 -8
  47. data/lib/sym/extensions/stdlib.rb +0 -1
  48. data/lib/sym/extensions/with_retry.rb +1 -1
  49. data/lib/sym/extensions/with_timeout.rb +1 -1
  50. data/lib/sym/version.rb +54 -5
  51. data/sym.gemspec +36 -35
  52. metadata +102 -66
  53. data/.codeclimate.yml +0 -30
  54. data/README.md +0 -623
  55. data/lib/sym/app/password/providers/drb_provider.rb +0 -41
@@ -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/
Binary file
@@ -32,7 +32,7 @@ puts data ? \
32
32
  Sym::App::KeyChain.new(key_name).send(action.to_sym, data) :
33
33
  Sym::App::KeyChain.new(key_name).send(action.to_sym)
34
34
  rescue StandardError => e
35
- STDERR.puts "#{e.message.red}"
35
+ warn "#{e.message.red}"
36
36
  end
37
37
 
38
38
 
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
- STDERR.puts "Interrupt, #{e.message}, exiting."
17
+ warn "Interrupt, #{e.message}, exiting."
15
18
  STDERR.flush
16
19
  exit 1
17
20
  end
@@ -0,0 +1,7 @@
1
+ ruby_version = RbConfig::CONFIG['MAJOR'].to_i * 10 + RbConfig::CONFIG['MINOR'].to_i
2
+ if ruby_version >= 27
3
+ Warning[:deprecated] = false
4
+ ENV['RUBYOPT'] = '-W:no-deprecated'
5
+ else
6
+ ENV['RUBYOPT']="-W0"
7
+ end
data/lib/sym.rb CHANGED
@@ -14,24 +14,18 @@ Sym::Configuration.configure do |config|
14
14
  config.compression_enabled = true
15
15
  config.compression_level = Zlib::BEST_COMPRESSION
16
16
  config.encrypted_file_extension = 'enc'
17
- config.default_key_file = Sym::Constants::SYM_KEY_FILE
17
+ config.default_key_file = Sym::Constants.sym_key_file
18
18
 
19
19
  config.password_cache_timeout = 300
20
20
 
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
  }
@@ -18,7 +18,6 @@ module Sym
18
18
  class << self
19
19
  attr_accessor :exit_code
20
20
  attr_accessor :stdin, :stdout, :stderr
21
-
22
21
  end
23
22
 
24
23
  self.exit_code = 0
@@ -66,7 +65,7 @@ module Sym
66
65
  self.exit_code = 1
67
66
  end
68
67
 
69
- def self.is_osx?
68
+ def self.osx?
70
69
  Gem::Platform.local.os.eql?('darwin')
71
70
  end
72
71
 
@@ -12,7 +12,7 @@ module Sym
12
12
 
13
13
  def initialize(opts)
14
14
  self.opts = opts
15
- self.selected_options = opts.keys.reject { |k| !opts[k] }
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.reject { |k| !opts[k] }
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
@@ -56,11 +56,10 @@ module Sym
56
56
  # brings in #parse(Array[String] args)
57
57
  include CLISlop
58
58
 
59
- attr_accessor :opts, :application, :outputs, :stdin, :stdout, :stderr, :kernel
60
-
59
+ attr_accessor :opts, :application, :outputs, :stdin, :stdout, :stderr, :kernel, :args
61
60
 
62
61
  def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = nil)
63
-
62
+ self.args = argv
64
63
  self.stdin = stdin
65
64
  self.stdout = stdout
66
65
  self.stderr = stderr
@@ -72,37 +71,46 @@ module Sym
72
71
 
73
72
  begin
74
73
  # Re-map any legacy options to the new options
75
- self.opts = parse(argv)
74
+ self.opts = parse(args)
75
+
76
+ if opts[:user_home]
77
+ Constants.user_home = opts[:user_home]
78
+ raise InvalidSymHomeDirectory, "#{opts[:user_home]} does not exist!" unless Dir.exist?(Constants.user_home)
79
+ end
80
+
81
+ # Deal with SYM_ARGS and -A
76
82
  if opts[:sym_args]
77
- append_sym_args(argv)
78
- self.opts = parse(argv)
83
+ if non_empty_array?(sym_args)
84
+ args << sym_args
85
+ args.flatten!
86
+ args.compact!
87
+ args.delete('-A')
88
+ args.delete('--sym-args')
89
+ self.opts = parse(args)
90
+ end
79
91
  end
80
92
 
81
93
  # Disable coloring if requested, or if piping STDOUT
82
94
  if opts[:no_color] || !self.stdout.tty?
83
95
  Colored2.disable! # reparse options without the colors to create new help msg
84
- self.opts = parse(argv)
96
+ self.opts = parse(args)
85
97
  end
86
98
 
87
99
  rescue StandardError => e
88
100
  log :error, "#{e.message}" if opts
89
101
  error exception: e
90
- exit 127 if stdin == STDIN
102
+ quit!(127) if stdin == STDIN
91
103
  end
92
104
 
93
- self.application = ::Sym::Application.new(opts, stdin, stdout, stderr, kernel)
105
+ self.application = ::Sym::Application.new(self.opts, stdin, stdout, stderr, kernel)
94
106
  end
95
107
 
96
- def append_sym_args(argv)
97
- if env_args = sym_args
98
- argv << env_args.split(' ')
99
- argv.flatten!
100
- argv.compact!
101
- end
108
+ def quit!(code = 0)
109
+ exit(code)
102
110
  end
103
111
 
104
112
  def sym_args
105
- ENV[Sym::Constants::ENV_ARGS_VARIABLE_NAME]
113
+ (ENV['SYM_ARGS']&.split(/\s+/) || [])
106
114
  end
107
115
 
108
116
  def execute!
@@ -132,15 +140,20 @@ module Sym
132
140
  end
133
141
 
134
142
  def opts_present
135
- o = opts.to_hash
136
- o.keys.map { |k| opts[k] ? nil : k }.compact.each { |k| o.delete(k) }
137
- o
143
+ opts.to_hash.tap do |o|
144
+ o.keys.map { |k| opts[k] ? nil : k }.compact.each { |k| o.delete(k) }
145
+ end
146
+ end
147
+
148
+ def log(*args)
149
+ Sym::App.log(*args, **opts.to_hash)
138
150
  end
139
151
 
152
+
140
153
  private
141
154
 
142
- def log(*args)
143
- Sym::App.log(*args, **(opts.to_hash))
155
+ def non_empty_array?(object)
156
+ object.is_a?(Array) && !object.empty?
144
157
  end
145
158
 
146
159
  def error(hash)
@@ -55,7 +55,8 @@ 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.is_osx?
58
+
59
+ if Sym::App.osx?
59
60
  o.string '-x', '--keychain', '[key-name] '.blue + 'write the key to OS-X Keychain'
60
61
  end
61
62
 
@@ -67,7 +68,7 @@ module Sym
67
68
  o.separator ' '
68
69
  o.separator 'Password Cache:'.yellow
69
70
  o.bool '-c', '--cache-passwords', ' enable password cache'
70
- o.integer '-u', '--cache-timeout', '[seconds]'.blue + ' expire passwords after'
71
+ o.integer '-z', '--cache-timeout', '[seconds]'.blue + ' expire passwords after'
71
72
  o.string '-r', '--cache-provider', '[provider]'.blue + ' cache provider, one of ' + "#{Sym::App::Password::Providers.provider_list}"
72
73
 
73
74
  o.separator ' '
@@ -89,8 +90,14 @@ module Sym
89
90
 
90
91
  o.separator ' '
91
92
  o.separator 'Utility:'.yellow
93
+ o.separator " The following flag helps with Sym installation by hooking to \n" +
94
+ " your #{'~/.bashrc'.bold.yellow}. If you are running Sym on an environment without \n" +
95
+ " user home available, you may need to force set user's home to any existing\n" +
96
+ " directory using the #{'--user-home'.bold.blue} flag.\n"
97
+
92
98
  o.string '-B', '--bash-support', '[file]'.blue + ' append bash completion & utils to a file'+ "\n" +
93
99
  ' such as ~/.bash_profile or ~/.bashrc'
100
+ o.string '-u', '--user-home', '[DIR]'.blue + " Overrides #{'${HOME}'.green} ==> supports AWS Lambda\n"
94
101
 
95
102
  o.separator ' '
96
103
  o.separator 'Help & Examples:'.yellow
@@ -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
 
@@ -73,7 +73,7 @@ module Sym
73
73
  end
74
74
 
75
75
  def add_to_keychain_if_needed(key)
76
- if opts[:keychain] && Sym::App.is_osx?
76
+ if opts[:keychain] && Sym::App.osx?
77
77
  Sym::App::KeyChain.new(opts[:keychain], opts).add(key)
78
78
  else
79
79
  key
@@ -15,7 +15,7 @@ module Sym
15
15
  file = opts[:bash_support]
16
16
 
17
17
  out = ''
18
- Sym::Constants::Bash::Config.each_pair do |key, config|
18
+ Sym::Constants.config.each_pair do |key, config|
19
19
  script_name = key.to_s
20
20
 
21
21
  # This removes the old version of this file.
@@ -43,7 +43,7 @@ module Sym
43
43
  out << "\nPlease reload your terminal session to activate bash completion\n"
44
44
  out << "and other installed BASH utilities.\n"
45
45
  out << "\nAlternatively, just type #{"source #{file}".bold.green} to reload BASH.\n"
46
- out << "Also — go ahead and try running #{"sym -h".bold.blue} and #{"symit -h".bold.blue}.\n"
46
+ out << "Also — go ahead and try running #{'sym -h'.bold.blue} and #{'symit -h'.bold.blue}.\n"
47
47
  end
48
48
 
49
49
  private
@@ -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?(Fixnum) }.map { |d| '%02d' % d }[0..-3].reverse.join
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
- encrypted_key, password = encrypt_with_password(the_key)
20
- add_password_to_the_cache(encrypted_key, password)
21
- the_key = encrypted_key
22
- end
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.is_osx?
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
- raise Sym::Errors::CantReadPasswordNoTTY.new('key requires a password, however STDIN is not a TTY') unless stdin.tty?
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
 
@@ -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
- execute command(:add, "-U -w '#{password}' ")
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.green}" if opts[:verbose]
61
+ puts "> #{command.yellow}" if opts[:verbose]
60
62
  output = `#{command}`
61
63
  result = $?
62
- raise Sym::Errors::KeyChainCommandError.new("Command error: #{result}, command: #{command}") unless result.success?
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.is_osx?
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 '#{self.class.user}' ",
91
- "-D '#{self.class.kind}' ",
92
- "-s '#{self.key_name}' "
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
@@ -7,7 +7,8 @@ module Sym
7
7
  required_option :quiet
8
8
 
9
9
  def output_proc
10
- ->(*) { ; }
10
+ ->(*) do
11
+ end
11
12
  end
12
13
  end
13
14
  end
@@ -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