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.
- checksums.yaml +4 -4
- data/README.md +5 -5
- data/bin/kbsecret +6 -14
- data/lib/kbsecret/cli/command/abstract.rb +51 -0
- data/lib/kbsecret/cli/command/conf.rb +42 -0
- data/lib/kbsecret/cli/command/cp.rb +54 -0
- data/lib/kbsecret/cli/command/dump_fields.rb +53 -0
- data/lib/kbsecret/cli/command/env.rb +66 -0
- data/lib/kbsecret/cli/command/generator.rb +62 -0
- data/lib/kbsecret/cli/command/generators.rb +37 -0
- data/lib/kbsecret/cli/command/list.rb +48 -0
- data/lib/kbsecret/cli/command/login.rb +66 -0
- data/lib/kbsecret/cli/command/new.rb +82 -0
- data/lib/kbsecret/cli/command/pass.rb +51 -0
- data/lib/kbsecret/cli/command/raw_edit.rb +46 -0
- data/lib/kbsecret/cli/command/rm.rb +58 -0
- data/lib/kbsecret/cli/command/session.rb +128 -0
- data/lib/kbsecret/cli/command/sessions.rb +47 -0
- data/lib/kbsecret/cli/command/stash_file.rb +62 -0
- data/lib/kbsecret/cli/command/todo.rb +78 -0
- data/lib/kbsecret/cli/command.rb +45 -0
- data/lib/kbsecret/cli.rb +6 -4
- data/lib/kbsecret/config.rb +4 -3
- data/lib/kbsecret/record/abstract.rb +2 -2
- data/lib/kbsecret/record.rb +9 -7
- data/lib/kbsecret/version.rb +1 -1
- metadata +22 -20
- data/lib/kbsecret/cli/kbsecret-conf +0 -31
- data/lib/kbsecret/cli/kbsecret-cp +0 -44
- data/lib/kbsecret/cli/kbsecret-dump-fields +0 -39
- data/lib/kbsecret/cli/kbsecret-env +0 -53
- data/lib/kbsecret/cli/kbsecret-generator +0 -42
- data/lib/kbsecret/cli/kbsecret-generators +0 -28
- data/lib/kbsecret/cli/kbsecret-list +0 -36
- data/lib/kbsecret/cli/kbsecret-login +0 -53
- data/lib/kbsecret/cli/kbsecret-new +0 -68
- data/lib/kbsecret/cli/kbsecret-pass +0 -37
- data/lib/kbsecret/cli/kbsecret-raw-edit +0 -31
- data/lib/kbsecret/cli/kbsecret-rm +0 -44
- data/lib/kbsecret/cli/kbsecret-session +0 -105
- data/lib/kbsecret/cli/kbsecret-sessions +0 -38
- data/lib/kbsecret/cli/kbsecret-stash-file +0 -48
- data/lib/kbsecret/cli/kbsecret-todo +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a564bac0dc6d8ba790b99238519dd12c2e1efea7fccfbf832a69e9861cb98f7
|
4
|
+
data.tar.gz: 502c1bb72096256a854631bf375a30f917f04fd636b28e186ce5b5294f8830e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
13
|
-
* [Quick start guide](https://kbsecret.github.io/
|
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/
|
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
|
-
$
|
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 +
|
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
|
-
|
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
|
-
|
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
|
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
|