kbsecret 1.2.0 → 1.3.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  3. data/bin/kbsecret +6 -14
  4. data/lib/kbsecret/cli/command/abstract.rb +51 -0
  5. data/lib/kbsecret/cli/command/conf.rb +42 -0
  6. data/lib/kbsecret/cli/command/cp.rb +54 -0
  7. data/lib/kbsecret/cli/command/dump_fields.rb +53 -0
  8. data/lib/kbsecret/cli/command/env.rb +66 -0
  9. data/lib/kbsecret/cli/command/generator.rb +62 -0
  10. data/lib/kbsecret/cli/command/generators.rb +37 -0
  11. data/lib/kbsecret/cli/command/list.rb +48 -0
  12. data/lib/kbsecret/cli/command/login.rb +66 -0
  13. data/lib/kbsecret/cli/command/new.rb +82 -0
  14. data/lib/kbsecret/cli/command/pass.rb +51 -0
  15. data/lib/kbsecret/cli/command/raw_edit.rb +46 -0
  16. data/lib/kbsecret/cli/command/rm.rb +58 -0
  17. data/lib/kbsecret/cli/command/session.rb +128 -0
  18. data/lib/kbsecret/cli/command/sessions.rb +47 -0
  19. data/lib/kbsecret/cli/command/stash_file.rb +62 -0
  20. data/lib/kbsecret/cli/command/todo.rb +78 -0
  21. data/lib/kbsecret/cli/command.rb +45 -0
  22. data/lib/kbsecret/cli.rb +6 -4
  23. data/lib/kbsecret/config.rb +4 -3
  24. data/lib/kbsecret/record/abstract.rb +2 -2
  25. data/lib/kbsecret/record.rb +9 -7
  26. data/lib/kbsecret/version.rb +1 -1
  27. metadata +22 -20
  28. data/lib/kbsecret/cli/kbsecret-conf +0 -31
  29. data/lib/kbsecret/cli/kbsecret-cp +0 -44
  30. data/lib/kbsecret/cli/kbsecret-dump-fields +0 -39
  31. data/lib/kbsecret/cli/kbsecret-env +0 -53
  32. data/lib/kbsecret/cli/kbsecret-generator +0 -42
  33. data/lib/kbsecret/cli/kbsecret-generators +0 -28
  34. data/lib/kbsecret/cli/kbsecret-list +0 -36
  35. data/lib/kbsecret/cli/kbsecret-login +0 -53
  36. data/lib/kbsecret/cli/kbsecret-new +0 -68
  37. data/lib/kbsecret/cli/kbsecret-pass +0 -37
  38. data/lib/kbsecret/cli/kbsecret-raw-edit +0 -31
  39. data/lib/kbsecret/cli/kbsecret-rm +0 -44
  40. data/lib/kbsecret/cli/kbsecret-session +0 -105
  41. data/lib/kbsecret/cli/kbsecret-sessions +0 -38
  42. data/lib/kbsecret/cli/kbsecret-stash-file +0 -48
  43. data/lib/kbsecret/cli/kbsecret-todo +0 -47
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3b7e55fb17c53bed43e18cbd8c7a8c57aaacce276af208638d5110284f38f763
4
- data.tar.gz: 3c5ea62a9a2d7d418d26369a1a55d6af2119d9328d6a86455d46be29e419e377
3
+ metadata.gz: 5a564bac0dc6d8ba790b99238519dd12c2e1efea7fccfbf832a69e9861cb98f7
4
+ data.tar.gz: 502c1bb72096256a854631bf375a30f917f04fd636b28e186ce5b5294f8830e5
5
5
  SHA512:
6
- metadata.gz: bb715c6c6602411bfe70f150c1c600cd51b7513a4b4433147b75df2f2b70df9d35345a9d85698acb8db37c450e43b8cb2823e9346c8b0d2a93d7397e6353e06c
7
- data.tar.gz: 6799876c9f29cd4b5ce9f39d5ec05329e6d1c3ea25e7cf49d0dd49e2d915ede5725d05222d5b68fc4d4bf9ca274195f671755f2658bc19f5bcc30974473b8156
6
+ metadata.gz: 8a508c3a12446379ba2947d78950f506214ee3c0f8240279cbc09592f44b119af150611a6d1addb18d5173cdb877da95ea9d1553ce0a0b2eef448b963abc47b2
7
+ data.tar.gz: 87c3e9ba9f42a2c5372b40a16bc7b8ce33042d34955e9ac65dff6fede1169be9214644c2d91f8b15d7e6d3eed19b1274a79c75cf2c6ac8e8f8d20d12b424f722
data/README.md CHANGED
@@ -9,11 +9,11 @@ KBSecret is a command line utility and library for managing *secrets*.
9
9
 
10
10
  Quick links:
11
11
 
12
- * [Installation instructions](https://kbsecret.github.io/installation)
13
- * [Quick start guide](https://kbsecret.github.io/quickstart)
14
- * [CLI documentation](https://kbsecret.github.io/man/)
12
+ * [Installation instructions](https://kbsecret.github.io/#/install/)
13
+ * [Quick start guide](https://kbsecret.github.io/#/quickstart/)
14
+ * [CLI documentation](https://kbsecret.github.io/man/kbsecret.1.html)
15
15
  * [API documentation](http://www.rubydoc.info/gems/kbsecret/)
16
- * [Customizing your installation](https://kbsecret.github.io/customization)
16
+ * [Customizing your installation](https://kbsecret.github.io/#/customize/)
17
17
 
18
18
  ## Hacking on KBSecret
19
19
 
@@ -22,7 +22,7 @@ Want to hack on KBSecret? Here's how you can get started:
22
22
  ```bash
23
23
  $ git clone git@github.com:kbsecret/kbsecret.git && cd kbsecret
24
24
  $ bundle install --path vendor/bundle
25
- $ RUBYLIB=./lib PATH=./bin:${PATH} bundle exec ./bin/kbsecret help
25
+ $ bundle exec ./bin/kbsecret help
26
26
  ```
27
27
 
28
28
  ### Manual Pages
data/bin/kbsecret CHANGED
@@ -6,12 +6,6 @@ require "fileutils"
6
6
 
7
7
  BUILTIN_CMDS = %w[help version commands types].freeze
8
8
 
9
- INTERNAL_CMD_PATH = File.join(File.dirname(__dir__), "lib/kbsecret/cli")
10
-
11
- INTERNAL_PATHS = Dir[File.join(INTERNAL_CMD_PATH, "*")].freeze
12
-
13
- INTERNAL_CMDS = INTERNAL_PATHS.map { |p| File.basename(p).sub!("kbsecret-", "") }
14
-
15
9
  EXT_PATHS = ENV["PATH"].split(File::PATH_SEPARATOR).map do |path|
16
10
  Dir[File.join(path, "kbsecret-*")]
17
11
  end.flatten.uniq.freeze
@@ -27,7 +21,7 @@ ALIASES = Hash.new { |_, k| k }.update(
27
21
  "-v" => "version"
28
22
  ).freeze
29
23
 
30
- ALL_CMDS = (ALIASES.keys + BUILTIN_CMDS + INTERNAL_CMDS + EXT_CMDS).freeze
24
+ ALL_CMDS = (ALIASES.keys + BUILTIN_CMDS + KBSecret::CLI::Command.command_names + EXT_CMDS).freeze
31
25
 
32
26
  KBSECRET_HELP = <<~KBSECRET_HELP
33
27
  Usage:
@@ -50,7 +44,7 @@ def migrate_configs
50
44
  end
51
45
 
52
46
  def internal?(cmd)
53
- INTERNAL_CMDS.include?(cmd)
47
+ KBSecret::CLI::Command.command_names.include?(cmd)
54
48
  end
55
49
 
56
50
  def external?(cmd)
@@ -72,11 +66,7 @@ end
72
66
  def expand(cmd)
73
67
  return cmd if alias?(cmd) || builtin?(cmd)
74
68
 
75
- if internal? cmd
76
- File.join(INTERNAL_CMD_PATH, "kbsecret-#{cmd}")
77
- else
78
- "kbsecret-#{cmd}"
79
- end
69
+ "kbsecret-#{cmd}"
80
70
  end
81
71
 
82
72
  def help(*args)
@@ -148,7 +138,9 @@ args = KBSecret::Config.command_args(command) + ARGV
148
138
 
149
139
  if builtin?(command)
150
140
  send command, *args
151
- elsif external?(command) || internal?(command)
141
+ elsif internal?(command)
142
+ KBSecret::CLI::Command.run! command, *args
143
+ elsif external?(command)
152
144
  exec expand(command), *args
153
145
  else
154
146
  KBSecret::CLI.die "Unknown command: '#{command}'."
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # Represents an abstract {KBSecret} command that can be subclassed to produce a more
7
+ # useful command. {KBSecret::CLI::Command::List} is an example of this.
8
+ # @abstract
9
+ class Abstract
10
+ # @return [CLI] the CLI state corresponding to the command
11
+ attr_reader :cli
12
+
13
+ # @return [String] the command's CLI-friendly name
14
+ # @example
15
+ # KBSecret::CLI::Command::StashFile # => "stash-file"
16
+ def self.command_name
17
+ name.split("::")
18
+ .last
19
+ .gsub(/([^A-Z])([A-Z]+)/, '\1-\2')
20
+ .downcase
21
+ end
22
+
23
+ # @param argv [String] the arguments to call the command with
24
+ def initialize(argv)
25
+ @cli = CLI.create(argv) { |_o| nil }
26
+ @cli.guard do
27
+ yield @cli if block_given?
28
+ end
29
+ end
30
+
31
+ # Sets up any state used by the command. Implemented by children.
32
+ # @abstract
33
+ def setup!
34
+ nil
35
+ end
36
+
37
+ # Runs any validation checks required by the command. Implemented by children.
38
+ # @abstract
39
+ def validate!
40
+ nil
41
+ end
42
+
43
+ # Runs the command. Implemented by children.
44
+ # @abstract
45
+ def run!
46
+ nil
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret conf`.
7
+ class Conf < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage: kbsecret conf [options]
13
+ HELP
14
+
15
+ o.bool "-c", "--commands", "open the commands config (commands.ini)"
16
+ o.bool "-d", "--directory", "print the path to the config directory"
17
+ o.bool "-r", "--record-directory", "print the path to the custom record directory"
18
+ end
19
+ end
20
+ end
21
+
22
+ # @see Command::Abstract#validate!
23
+ def validate!
24
+ cli.die "Missing $EDITOR." unless ENV["EDITOR"]
25
+ end
26
+
27
+ # @see Command::Abstract#run!
28
+ def run!
29
+ if cli.opts.commands?
30
+ exec "#{ENV["EDITOR"]} #{Config::COMMAND_CONFIG_FILE}"
31
+ elsif cli.opts.directory?
32
+ puts Config::CONFIG_DIR
33
+ elsif cli.opts.record_directory?
34
+ puts Config::CUSTOM_TYPES_DIR
35
+ else
36
+ exec "#{ENV["EDITOR"]} #{Config::CONFIG_FILE}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret cp`.
7
+ class Cp < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret cp [options] <source> <destination> <record [record ...]>
14
+ HELP
15
+
16
+ o.bool "-f", "--force", "force copying (ignore overwrites)"
17
+ o.bool "-m", "--move", "delete the record after copying"
18
+ end
19
+
20
+ cli.dreck do
21
+ string :src_sess
22
+ string :dst_sess
23
+ list :string, :labels
24
+ end
25
+ end
26
+ end
27
+
28
+ # @see Command::Abstract#run!
29
+ def run!
30
+ cli.guard do
31
+ src_sess = Session[cli.args[:src_sess]]
32
+ dst_sess = Session[cli.args[:dst_sess]]
33
+
34
+ selected_records = src_sess.records.select { |r| cli.args[:labels].include?(r.label) }
35
+ cli.die "No such record(s)." if selected_records.empty?
36
+
37
+ overlaps = dst_sess.record_labels & selected_records.map(&:label)
38
+
39
+ # the code below actually handles the overwriting if necessary, but we fail early here
40
+ # for friendliness and to avoid half-copying the selected records
41
+ unless overlaps.empty? || cli.opts.force?
42
+ cli.die "Refusing to overwrite existing record(s) without --force."
43
+ end
44
+
45
+ selected_records.each do |record|
46
+ dst_sess.import_record(record, overwrite: cli.opts.force?)
47
+ src_sess.delete_record(label) if cli.opts.move?
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret dump-fields`.
7
+ class DumpFields < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret dump-fields [options] <record>
14
+ HELP
15
+
16
+ o.string "-s", "--session", "the session to search in", default: :default
17
+ o.bool "-x", "--terse", "output in field<sep>value format"
18
+ o.string "-i", "--ifs", "separate terse pairs with this string", default: CLI.ifs
19
+ end
20
+
21
+ cli.dreck do
22
+ string :label
23
+ end
24
+
25
+ cli.ensure_session!
26
+ end
27
+ end
28
+
29
+ # @see Command::Abstract#setup!
30
+ def setup!
31
+ @record = cli.session[cli.args[:label]]
32
+ end
33
+
34
+ # @see Command::Abstract#validate!
35
+ def validate!
36
+ cli.die "No such record." unless @record
37
+ end
38
+
39
+ # @see Command::Abstract#run!
40
+ def run!
41
+ field_values = @record.data_fields.map { |f| @record.send f }
42
+ field_pairs = @record.data_fields.zip(field_values)
43
+
44
+ if cli.opts.terse?
45
+ puts field_pairs.map { |f, v| "#{f}#{cli.opts[:ifs]}#{v}" }.join "\n"
46
+ else
47
+ puts field_pairs.map { |f, v| "#{f}: #{v}" }.join "\n"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret env`.
7
+ class Env < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret env [options] <record [record ...]>
14
+ HELP
15
+
16
+ o.string "-s", "--session", "the session to search in", default: :default
17
+ o.bool "-a", "--all", "retrieve all environment records, not just listed ones"
18
+ o.bool "-v", "--value-only", "print only the environment value, not the key"
19
+ o.bool "-n", "--no-export", "print only VAR=val keypairs without `export`"
20
+ o.bool "-u", "--unescape-plus", "escape any pluses in the variable and/or value"
21
+ end
22
+
23
+ unless cli.opts.all?
24
+ cli.dreck do
25
+ list :string, :labels
26
+ end
27
+ end
28
+
29
+ cli.ensure_session!
30
+ end
31
+ end
32
+
33
+ # @see Command::Abstract#setup!
34
+ def setup!
35
+ @records = if cli.opts.all?
36
+ cli.session.records :environment
37
+ else
38
+ cli.session.records(:environment).select do |record|
39
+ cli.args[:labels].include? record.label
40
+ end
41
+ end
42
+ end
43
+
44
+ # @see Command::Abstract#validate!
45
+ def validate!
46
+ cli.die "No such record(s)." if @records.empty?
47
+ end
48
+
49
+ # @see Command::Abstract#run!
50
+ def run!
51
+ env_output = if cli.opts.no_export?
52
+ @records.map(&:to_assignment).join(" ")
53
+ elsif cli.opts.value_only?
54
+ @records.map(&:value).join("\n")
55
+ else
56
+ @records.map(&:to_export).join("\n")
57
+ end
58
+
59
+ env_output.gsub!("\\+", "+") if cli.opts.unescape_plus?
60
+
61
+ puts env_output
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret generator`.
7
+ class Generator < Abstract
8
+ # The list of subcommands supported by `kbsecret generator`.
9
+ SUBCOMMANDS = %w[new rm].freeze
10
+
11
+ def initialize(argv)
12
+ super(argv) do |cli|
13
+ cli.slop cmds: SUBCOMMANDS do |o|
14
+ o.banner = <<~HELP
15
+ Usage:
16
+ kbsecret generator [options] <new|rm> <generator>
17
+ HELP
18
+
19
+ o.string "-F", "--format", "the format of the secrets generated", default: "hex"
20
+ o.integer "-l", "--length", "the length, in bytes, of the secrets generated",
21
+ default: 16
22
+ o.bool "-f", "--force", "force generator creation (ignore overwrite)"
23
+ end
24
+
25
+ cli.dreck do
26
+ string :command
27
+ string :generator
28
+ end
29
+
30
+ cli.ensure_generator! :argument if cli.args[:command] == "rm"
31
+ end
32
+ end
33
+
34
+ # @see Command::Abstract#setup!
35
+ def setup!
36
+ @subcmd = cli.args[:command]
37
+ end
38
+
39
+ # @see Command::Abstract#validate!
40
+ def validate!
41
+ cli.die "Unknown subcommand: #{@subcmd}." unless SUBCOMMANDS.include?(@subcmd)
42
+ end
43
+
44
+ # @see Command::Abstract#run!
45
+ def run!
46
+ case @subcmd
47
+ when "new"
48
+ if Config.generator?(cli.args[:generator]) && !cli.opts.force?
49
+ cli.die "Refusing to overwrite an existing generator without --force."
50
+ end
51
+
52
+ Config.configure_generator(cli.args[:generator],
53
+ format: cli.opts[:format],
54
+ length: cli.opts[:length])
55
+ when "rm"
56
+ Config.deconfigure_generator(cli.args[:generator])
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret generators`.
7
+ class Generators < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret generators [options]
14
+ HELP
15
+
16
+ o.bool "-a", "--show-all", "show each generator in depth (i.e. metadata)"
17
+ end
18
+ end
19
+ end
20
+
21
+ # @see Command::Abstract#run!
22
+ def run!
23
+ Config[:generators].each do |label, config|
24
+ puts label
25
+
26
+ next unless cli.opts.show_all?
27
+
28
+ puts <<~DETAIL
29
+ \tFormat: #{config[:format]}
30
+ \tLength: #{config[:length]}
31
+ DETAIL
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret list`.
7
+ class List < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret list [options]
14
+ HELP
15
+
16
+ o.string "-s", "--session", "the session to list from", default: :default
17
+ o.string "-t", "--type", "the type of secrets to list", default: nil
18
+ o.bool "-a", "--show-all", "show everything in each secret (i.e. metadata)"
19
+ end
20
+
21
+ cli.ensure_type! if cli.opts[:type]
22
+ cli.ensure_session!
23
+ end
24
+ end
25
+
26
+ # @see Command::Abstract#setup!
27
+ def setup!
28
+ @records = cli.session.records cli.opts[:type]
29
+ end
30
+
31
+ # @see Command::Abstract#setup!
32
+ def run!
33
+ @records.each do |record|
34
+ puts record.label
35
+
36
+ next unless cli.opts.show_all?
37
+
38
+ puts <<~DETAIL
39
+ \tType: #{record.type}
40
+ \tLast changed: #{Time.at(record.timestamp)}
41
+ \tRaw data: #{record.data}
42
+ DETAIL
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KBSecret
4
+ class CLI
5
+ module Command
6
+ # The implementation of `kbsecret login`.
7
+ class Login < Abstract
8
+ def initialize(argv)
9
+ super(argv) do |cli|
10
+ cli.slop do |o|
11
+ o.banner = <<~HELP
12
+ Usage:
13
+ kbsecret login [options] <record [record ...]>
14
+ HELP
15
+
16
+ o.string "-s", "--session", "the session to search in", default: :default
17
+ o.bool "-a", "--all", "retrieve all login records, not just listed ones"
18
+ o.bool "-x", "--terse", "output in label<sep>username<sep>password format"
19
+ o.string "-i", "--ifs", "separate terse fields with this string", default: CLI.ifs
20
+ end
21
+
22
+ unless cli.opts.all?
23
+ cli.dreck do
24
+ list :string, :labels
25
+ end
26
+ end
27
+
28
+ cli.ensure_session!
29
+ end
30
+ end
31
+
32
+ # @see Command::Abstract#setup!
33
+ def setup!
34
+ @records = if cli.opts.all?
35
+ cli.session.records :login
36
+ else
37
+ cli.session.records(:login).select do |record|
38
+ cli.args[:labels].include? record.label
39
+ end
40
+ end
41
+ end
42
+
43
+ # @see Command::Abstract#validate!
44
+ def validate!
45
+ cli.die "No such record(s)." if @records.empty?
46
+ end
47
+
48
+ # @see Command::Abstract#run!
49
+ def run!
50
+ @records.each do |record|
51
+ if cli.opts.terse?
52
+ fields = %i[label username password].map { |m| record.send(m) }
53
+ puts fields.join(cli.opts[:ifs])
54
+ else
55
+ puts <<~DETAIL
56
+ Label: #{record.label}
57
+ \tUsername: #{record.username}
58
+ \tPassword: #{record.password}
59
+ DETAIL
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "abbrev"
5
+ require "tty-prompt"
6
+
7
+ module KBSecret
8
+ class CLI
9
+ module Command
10
+ # The implementation of `kbsecret new`.
11
+ class New < Abstract
12
+ def initialize(argv)
13
+ super(argv) do |cli|
14
+ cli.slop do |o|
15
+ o.banner = <<~HELP
16
+ Usage:
17
+ kbsecret new [options] <type> <label>
18
+ HELP
19
+
20
+ o.string "-s", "--session", "the session to contain the record", default: :default
21
+ o.bool "-f", "--force", "force creation (ignore overwrites, etc.)"
22
+ o.bool "-e", "--echo", "echo input to tty (only affects interactive input)"
23
+ o.bool "-G", "--generate", "generate secret fields (interactive only)"
24
+ o.string "-g", "--generator", "the generator to use for secret fields",
25
+ default: :default
26
+ o.bool "-x", "--terse", "read fields from input in a terse format"
27
+ o.string "-i", "--ifs", "separate terse fields with this string", default: CLI.ifs
28
+ end
29
+
30
+ cli.dreck do
31
+ string :type
32
+ string :label
33
+ end
34
+
35
+ cli.ensure_generator!
36
+ cli.ensure_type! :argument
37
+ cli.ensure_session!
38
+ end
39
+ end
40
+
41
+ # @see Command::Abstract#setup!
42
+ def setup!
43
+ @label = cli.args[:label]
44
+ @type = CLI::TYPE_ALIASES[cli.args[:type]]
45
+ end
46
+
47
+ # @see Command::Abstract#validate!
48
+ def validate!
49
+ # the code below actually handles the overwriting if necessary, but we fail early here
50
+ # for friendliness and to avoid prompting the user for input unnecessarily
51
+ if cli.session.record?(@label) && !cli.opts.force?
52
+ cli.die "Refusing to overwrite a record without --force."
53
+ end
54
+ end
55
+
56
+ # @see Command::Abstract#run!
57
+ def run!
58
+ generator = KBSecret::Generator.new(cli.opts[:generator]) if cli.opts.generate?
59
+
60
+ fields = if cli.opts.terse?
61
+ STDIN.read.chomp.split cli.opts[:ifs]
62
+ else
63
+ prompt = TTY::Prompt.new
64
+ klass = Record.class_for(@type)
65
+ klass.external_fields.map do |field|
66
+ if cli.opts.generate? && klass.sensitive?(field)
67
+ generator.secret
68
+ else
69
+ prompt.ask "#{field.capitalize}?",
70
+ echo: !klass.sensitive?(field) || cli.opts.echo?
71
+ end
72
+ end
73
+ end
74
+
75
+ cli.guard do
76
+ cli.session.add_record @type, @label, *fields, overwrite: cli.opts.force?
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end