kbsecret 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +1 -0
- data/LICENSE +21 -0
- data/README.md +66 -0
- data/bin/kbsecret +108 -0
- data/bin/kbsecret-env +50 -0
- data/bin/kbsecret-list +41 -0
- data/bin/kbsecret-login +54 -0
- data/bin/kbsecret-new +53 -0
- data/bin/kbsecret-new-session +40 -0
- data/bin/kbsecret-rm +45 -0
- data/bin/kbsecret-sessions +33 -0
- data/lib/kbsecret.rb +13 -0
- data/lib/kbsecret/config.rb +52 -0
- data/lib/kbsecret/exceptions.rb +10 -0
- data/lib/kbsecret/record.rb +30 -0
- data/lib/kbsecret/record/abstract.rb +61 -0
- data/lib/kbsecret/record/environment.rb +42 -0
- data/lib/kbsecret/record/login.rb +34 -0
- data/lib/kbsecret/record/unstructured.rb +22 -0
- data/lib/kbsecret/session.rb +69 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9587292e2be80b4a53d26f4bdda06e2a94d1b7be
|
4
|
+
data.tar.gz: 5dd01f1c1819f0f17537487ff73e621e27193938
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4227a95d277ed064a5cd1b4dd3add4dedcabb97d041c66c5dffec753a87ab0de73684582a4d18e34bd33c192912d54dc173527516daa10a172dbd19d55e73574
|
7
|
+
data.tar.gz: b14e1d1524a71d70e85185c7c61c43a3791923c5698273b68398b6dcf1d072a682a3f7bfb695666d1cd25819812d740e5cf150c71142571d741dd5e426091845
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --markup-provider=redcarpet --markup=markdown - *.md LICENSE
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 William Woodruff <william @ tuffbizz.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
kbsecret
|
2
|
+
========
|
3
|
+
|
4
|
+
kbsecret is a combined library/utility that provides a secret management
|
5
|
+
interface for [KBFS](https://keybase.io/docs/kbfs) and
|
6
|
+
[Keybase](https://keybase.io/).
|
7
|
+
|
8
|
+
### Benefits over current offerings
|
9
|
+
|
10
|
+
* Easy password and environment sharing across multiple users.
|
11
|
+
- `kbsecret -s dev-team login github` prints the dev team's GitHub login.
|
12
|
+
* No PGP/SSH key setup necessary - you only need a Keybase account.
|
13
|
+
- No more worrying about losing your key.
|
14
|
+
* Transparent access - KBFS provides a VFS layer over all reads/writes.
|
15
|
+
- All records are stored encrypted on KBFS.
|
16
|
+
|
17
|
+
### Installation
|
18
|
+
|
19
|
+
kbsecret is available via RubyGems:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ gem install kbsecret
|
23
|
+
```
|
24
|
+
|
25
|
+
For hacking:
|
26
|
+
|
27
|
+
```bash
|
28
|
+
$ git clone git@github.com:woodruffw/kbsecret.git && cd kbsecret
|
29
|
+
$ RUBYLIB=./lib PATH=${PATH}:./bin ./bin/kbsecret help
|
30
|
+
```
|
31
|
+
|
32
|
+
### Usage
|
33
|
+
|
34
|
+
```bash
|
35
|
+
# create a new login record under the default session
|
36
|
+
$ kbsecret new login gmail "foo@example.com" "barbazquux"
|
37
|
+
|
38
|
+
# list all records under the default session
|
39
|
+
$ kbsecret list
|
40
|
+
gmail
|
41
|
+
|
42
|
+
# show the requested login record
|
43
|
+
$ kbsecret login gmail
|
44
|
+
Label: gmail
|
45
|
+
Username: foo@example.com
|
46
|
+
Password: barbazquux
|
47
|
+
|
48
|
+
# create a new session between 3 keybase users (foo, bar, and baz)
|
49
|
+
$ kbsecret new-session -l dev-team -u foo,bar,baz
|
50
|
+
|
51
|
+
# list available sessions
|
52
|
+
$ kbsecret sessions
|
53
|
+
default
|
54
|
+
dev-team
|
55
|
+
|
56
|
+
# add an environment record to the dev-team session
|
57
|
+
$ kbsecret new environment API_KEY 0xBADBEEF -s dev-team
|
58
|
+
|
59
|
+
# list all records under the dev-team session
|
60
|
+
$ kbsecret list -s dev-team
|
61
|
+
API_KEY
|
62
|
+
|
63
|
+
# get all environment records in dev-team in an easy-to-source format
|
64
|
+
$ kbsecret env -s dev-team --all
|
65
|
+
export API_KEY='0xBADBEEF'
|
66
|
+
```
|
data/bin/kbsecret
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
|
5
|
+
BUILTIN_CMDS = [
|
6
|
+
"help",
|
7
|
+
"version",
|
8
|
+
].freeze
|
9
|
+
|
10
|
+
EXT_PATHS = ENV["PATH"].split(File::PATH_SEPARATOR).map do |path|
|
11
|
+
Dir[File.join(path, "kbsecret-*")]
|
12
|
+
end.flatten.freeze
|
13
|
+
|
14
|
+
EXT_CMDS = EXT_PATHS.map do |c|
|
15
|
+
File.basename(c, File.extname(c)).sub!("kbsecret-", "")
|
16
|
+
end.freeze
|
17
|
+
|
18
|
+
ALIASES = Hash.new { |_, k| k }.update({
|
19
|
+
"--help" => "help",
|
20
|
+
"-h" => "help",
|
21
|
+
"l" => "login",
|
22
|
+
"pw" => "login",
|
23
|
+
"del" => "rm",
|
24
|
+
"create" => "new",
|
25
|
+
}).freeze
|
26
|
+
|
27
|
+
ALL_CMDS = (ALIASES.keys + BUILTIN_CMDS + EXT_CMDS).freeze
|
28
|
+
|
29
|
+
def external?(cmd)
|
30
|
+
EXT_CMDS.include?(cmd)
|
31
|
+
end
|
32
|
+
|
33
|
+
def builtin?(cmd)
|
34
|
+
BUILTIN_CMDS.include?(cmd)
|
35
|
+
end
|
36
|
+
|
37
|
+
def alias?(cmd)
|
38
|
+
ALIASES.keys.include?(cmd)
|
39
|
+
end
|
40
|
+
|
41
|
+
def normalize(cmd)
|
42
|
+
ALIASES[cmd]
|
43
|
+
end
|
44
|
+
|
45
|
+
def expand(cmd)
|
46
|
+
return cmd if alias?(cmd) || builtin?(cmd)
|
47
|
+
"kbsecret-#{cmd}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def help(*args)
|
51
|
+
command = normalize args.shift
|
52
|
+
if command.nil?
|
53
|
+
puts <<~EOS
|
54
|
+
Usage:
|
55
|
+
kbsecret <command> <args ...>
|
56
|
+
|
57
|
+
Available commands:
|
58
|
+
#{ALL_CMDS.join(", ")}
|
59
|
+
|
60
|
+
More more information about a particular command, try:
|
61
|
+
kbsecret help <command>
|
62
|
+
EOS
|
63
|
+
else
|
64
|
+
if builtin? command
|
65
|
+
send "#{command}_help"
|
66
|
+
else
|
67
|
+
system expand(command), "--help"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# lol
|
73
|
+
def help_help
|
74
|
+
puts <<~EOS
|
75
|
+
Prints brief help for the given command.
|
76
|
+
|
77
|
+
Usage:
|
78
|
+
kbsecret help <command>
|
79
|
+
|
80
|
+
For a list of all commands, see:
|
81
|
+
kbsecret help
|
82
|
+
EOS
|
83
|
+
end
|
84
|
+
|
85
|
+
def version(*args)
|
86
|
+
puts <<~EOS
|
87
|
+
kbsecret version #{KBSecret::VERSION}.
|
88
|
+
EOS
|
89
|
+
end
|
90
|
+
|
91
|
+
def version_help
|
92
|
+
puts <<~EOS
|
93
|
+
Prints kbsecret's version.
|
94
|
+
|
95
|
+
Usage:
|
96
|
+
kbsecret version
|
97
|
+
EOS
|
98
|
+
end
|
99
|
+
|
100
|
+
command = normalize(ARGV.shift || "help")
|
101
|
+
|
102
|
+
if builtin? command
|
103
|
+
send command, *ARGV
|
104
|
+
elsif external? command
|
105
|
+
system expand(command), *ARGV
|
106
|
+
else
|
107
|
+
STDERR.puts "Fatal: Unknown command: '#{command}'."
|
108
|
+
end
|
data/bin/kbsecret-env
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
|
6
|
+
opts = Slop.parse suppress_errors: true do |o|
|
7
|
+
o.banner = <<~EOS
|
8
|
+
Retrieve environment records in a source-able format.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
kbsecret env [--session <name>] [--all] <label1 label2 ...>
|
12
|
+
|
13
|
+
Examples:
|
14
|
+
kbsecret env --all
|
15
|
+
kbsecret env staging beta
|
16
|
+
EOS
|
17
|
+
|
18
|
+
o.string "-s", "--session", "the session name", default: :default
|
19
|
+
o.bool "-a", "--all", "retrieve all environment records, not just listed ones"
|
20
|
+
|
21
|
+
o.on "-h", "--help" do
|
22
|
+
puts o
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
sess_name = opts[:session]
|
28
|
+
|
29
|
+
unless KBSecret::Config.session? sess_name
|
30
|
+
abort "Fatal: Unknown session: '#{sess_name}'."
|
31
|
+
end
|
32
|
+
|
33
|
+
session = KBSecret::Session.new label: sess_name
|
34
|
+
|
35
|
+
env_records = session.records.select { |r| r.type == "environment" }
|
36
|
+
|
37
|
+
if opts.all?
|
38
|
+
selected_records = env_records
|
39
|
+
else
|
40
|
+
selected_labels = opts.args.uniq
|
41
|
+
|
42
|
+
# instead of complaining about nonexistent records, just ignore them
|
43
|
+
selected_records = selected_labels.map do |l|
|
44
|
+
env_records.find { |r| r.label == l }
|
45
|
+
end.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
env_records.each do |record|
|
49
|
+
puts record.to_export
|
50
|
+
end
|
data/bin/kbsecret-list
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
|
6
|
+
opts = Slop.parse suppress_errors: true do |o|
|
7
|
+
o.banner = <<~EOS
|
8
|
+
List all secrets known to the specified session (or the default session).
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
kbsecret list [--session <name>] [--show-all]
|
12
|
+
EOS
|
13
|
+
|
14
|
+
o.string "-s", "--session", "the session name", default: :default
|
15
|
+
o.bool "-a", "--show-all", "show everything in each secret (i.e. metadata)"
|
16
|
+
|
17
|
+
o.on "-h", "--help" do
|
18
|
+
puts o
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
sess_name = opts[:session]
|
24
|
+
|
25
|
+
unless KBSecret::Config.session? sess_name
|
26
|
+
abort "Fatal: Unknown session: '#{sess_name}'."
|
27
|
+
end
|
28
|
+
|
29
|
+
session = KBSecret::Session.new label: sess_name
|
30
|
+
|
31
|
+
session.records.each do |record|
|
32
|
+
line = "#{record.label}\n"
|
33
|
+
line << <<~EOS if opts.show_all?
|
34
|
+
\tType: #{record.type}
|
35
|
+
\tLast changed: #{Time.at(record.timestamp)}
|
36
|
+
\tRaw data: #{record.data}
|
37
|
+
EOS
|
38
|
+
|
39
|
+
puts line
|
40
|
+
end
|
41
|
+
|
data/bin/kbsecret-login
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
|
6
|
+
opts = Slop.parse suppress_errors: true do |o|
|
7
|
+
o.banner = <<~EOS
|
8
|
+
Retrieve login records.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
kbsecret env [--session <name>] <label1 label2 ...>
|
12
|
+
|
13
|
+
Examples:
|
14
|
+
kbsecret login gmail
|
15
|
+
kbsecret login gmail netflix
|
16
|
+
EOS
|
17
|
+
|
18
|
+
o.bool "-a", "--all", "retrieve all login records, not just listed ones"
|
19
|
+
o.string "-s", "--session", "the session name", default: :default
|
20
|
+
|
21
|
+
o.on "-h", "--help" do
|
22
|
+
puts o
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
sess_name = opts[:session]
|
28
|
+
|
29
|
+
unless KBSecret::Config.session? sess_name
|
30
|
+
abort "Fatal: Unknown session: '#{sess_name}'."
|
31
|
+
end
|
32
|
+
|
33
|
+
session = KBSecret::Session.new label: sess_name
|
34
|
+
|
35
|
+
login_records = session.records.select { |r| r.type == "login" }
|
36
|
+
|
37
|
+
if opts.all?
|
38
|
+
selected_records = login_records
|
39
|
+
else
|
40
|
+
selected_labels = opts.args.uniq
|
41
|
+
|
42
|
+
# instead of complaining about nonexistent records, just ignore them
|
43
|
+
selected_records = selected_labels.map do |l|
|
44
|
+
login_records.find { |r| r.label == l }
|
45
|
+
end.compact
|
46
|
+
end
|
47
|
+
|
48
|
+
selected_records.each do |record|
|
49
|
+
puts <<~EOS
|
50
|
+
Label: #{record.label}
|
51
|
+
\tUsername: #{record.username}
|
52
|
+
\tPassword: #{record.password}
|
53
|
+
EOS
|
54
|
+
end
|
data/bin/kbsecret-new
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
|
6
|
+
opts = Slop.parse suppress_errors: true do |o|
|
7
|
+
o.banner = <<~EOS
|
8
|
+
Create a new secret record.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
kbsecret new [--session <name>] [--force] <type> <label> <args ...>
|
12
|
+
|
13
|
+
Examples:
|
14
|
+
kbsecret new login gmail "foo@bar.com" "mytopsecretpassword"
|
15
|
+
kbsecret new environment API_KEY "abcdef-0123456789"
|
16
|
+
EOS
|
17
|
+
|
18
|
+
o.string "-s", "--session", "the session name", default: :default
|
19
|
+
o.bool "-f", "--force", "force creation (ignore overwrites, etc.)"
|
20
|
+
|
21
|
+
o.on "-h", "--help" do
|
22
|
+
puts o
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
sess_name = opts[:session]
|
28
|
+
|
29
|
+
unless KBSecret::Config.session? sess_name
|
30
|
+
abort "Fatal: Unknown session: '#{sess_name}'."
|
31
|
+
end
|
32
|
+
|
33
|
+
abort "Fatal: Not enough arguments." if opts.args.size < 2
|
34
|
+
|
35
|
+
session = KBSecret::Session.new label: sess_name
|
36
|
+
type, label = opts.args.shift 2
|
37
|
+
|
38
|
+
if session.record?(label) && !opts.force?
|
39
|
+
abort "Fatal: Refusing to overwrite an existing record without --force."
|
40
|
+
end
|
41
|
+
|
42
|
+
unless KBSecret::Record.type?(type)
|
43
|
+
abort <<~EOS
|
44
|
+
Fatal: Unknown record type: '#{type}'.
|
45
|
+
Known types are: #{KBSecret::Record.record_types.join(", ")}.
|
46
|
+
EOS
|
47
|
+
end
|
48
|
+
|
49
|
+
begin
|
50
|
+
session.add_record(type, label, *opts.args)
|
51
|
+
rescue => e
|
52
|
+
abort "Fatal: #{e.to_s}."
|
53
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "keybase"
|
4
|
+
require "kbsecret"
|
5
|
+
require "slop"
|
6
|
+
|
7
|
+
opts = Slop.parse suppress_errors: true do |o|
|
8
|
+
o.banner = <<~EOS
|
9
|
+
Create a new session.
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
kbsecret new-session --label <label> --users <user1,...> --root <dir>
|
13
|
+
|
14
|
+
Example:
|
15
|
+
kbsecret new-session -l dev -u me,otherperson -r devsecrets
|
16
|
+
EOS
|
17
|
+
|
18
|
+
o.string "-l", "--label", "the session label", default: :default
|
19
|
+
o.array "-u", "--users", "the keybase users", default: [Keybase.current_user]
|
20
|
+
o.string "-r", "--root", "the secret root directory", default: "kbsecret"
|
21
|
+
o.bool "-f", "--force", "force creation (ignore overwrites, etc.)"
|
22
|
+
|
23
|
+
o.on "-h", "--help" do
|
24
|
+
puts o
|
25
|
+
exit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
session_label = opts[:label]
|
30
|
+
|
31
|
+
if KBSecret::Config.session?(session_label) && !opts.force?
|
32
|
+
abort "Fatal: Refusing to overwrite an existing session without --force."
|
33
|
+
end
|
34
|
+
|
35
|
+
session_hash = {
|
36
|
+
users: opts[:users],
|
37
|
+
root: opts[:root],
|
38
|
+
}
|
39
|
+
|
40
|
+
KBSecret::Config.add_session(session_label, session_hash)
|
data/bin/kbsecret-rm
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
require "tty-prompt"
|
6
|
+
|
7
|
+
$VERBOSE = nil # tty-prompt blasts us with irrelevant warnings on 2.4
|
8
|
+
|
9
|
+
opts = Slop.parse suppress_errors: true do |o|
|
10
|
+
o.banner = <<~EOS
|
11
|
+
Delete a record.
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
kbsecret rm [--session <name>] <label>
|
15
|
+
EOS
|
16
|
+
|
17
|
+
o.string "-s", "--session", "the session name", default: :default
|
18
|
+
o.bool "-i", "--interactive", "ask for confirmation before deleting"
|
19
|
+
|
20
|
+
o.on "-h", "--help" do
|
21
|
+
puts o
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sess_name = opts[:session]
|
27
|
+
|
28
|
+
unless KBSecret::Config.session? sess_name
|
29
|
+
abort "Fatal: Unknown session: '#{sess_name}'."
|
30
|
+
end
|
31
|
+
|
32
|
+
session = KBSecret::Session.new label: sess_name
|
33
|
+
|
34
|
+
label = opts.args.shift
|
35
|
+
|
36
|
+
abort "Fatal: I need the label of a record to delete." unless label
|
37
|
+
|
38
|
+
tty = TTY::Prompt.new
|
39
|
+
confirm = true
|
40
|
+
|
41
|
+
if opts.interactive?
|
42
|
+
confirm = tty.yes?("Delete '#{label}' from the #{session.label} session?")
|
43
|
+
end
|
44
|
+
|
45
|
+
session.delete_record(label) if confirm
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "kbsecret"
|
4
|
+
require "slop"
|
5
|
+
|
6
|
+
opts = Slop.parse suppress_errors: true do |o|
|
7
|
+
o.banner = <<~EOS
|
8
|
+
List all available sessions.
|
9
|
+
The sessions listed can be passed to other commands via the -s flag.
|
10
|
+
|
11
|
+
Usage:
|
12
|
+
kbsecret sessions [--show-all]
|
13
|
+
EOS
|
14
|
+
|
15
|
+
o.bool "-a", "--show-all", "show each session in depth (i.e. metadata)"
|
16
|
+
|
17
|
+
o.on "-h", "--help" do
|
18
|
+
puts o
|
19
|
+
exit
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
KBSecret::Config.session_names.each do |sess_name|
|
24
|
+
session_hash = KBSecret::Config.session(sess_name)
|
25
|
+
session = KBSecret::Session.new label: sess_name
|
26
|
+
line = "#{sess_name}\n"
|
27
|
+
line << <<~EOS if opts.show_all?
|
28
|
+
\tUsers: #{session_hash[:users].join(", ")}
|
29
|
+
\tSecrets root: #{session_hash[:root]} (#{session.directory})
|
30
|
+
EOS
|
31
|
+
|
32
|
+
puts line
|
33
|
+
end
|
data/lib/kbsecret.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require "keybase"
|
2
|
+
|
3
|
+
require_relative "kbsecret/config"
|
4
|
+
require_relative "kbsecret/exceptions"
|
5
|
+
require_relative "kbsecret/record"
|
6
|
+
require_relative "kbsecret/session"
|
7
|
+
|
8
|
+
module KBSecret
|
9
|
+
VERSION = "0.0.1".freeze
|
10
|
+
|
11
|
+
raise Keybase::KeybaseNotRunningError unless Keybase.running?
|
12
|
+
raise Keybase::KBFSNotRunningError unless Dir.exist?(Config[:mount])
|
13
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
module KBSecret
|
5
|
+
class Config
|
6
|
+
CONFIG_DIR = File.expand_path("~/.config/kbsecret").freeze
|
7
|
+
|
8
|
+
CONFIG_FILE = File.join(CONFIG_DIR, "config.yml").freeze
|
9
|
+
|
10
|
+
DEFAULT_CONFIG = {
|
11
|
+
mount: "/keybase",
|
12
|
+
|
13
|
+
sessions: {
|
14
|
+
default: {
|
15
|
+
users: [Keybase.current_user],
|
16
|
+
root: "kbsecret",
|
17
|
+
}
|
18
|
+
}
|
19
|
+
}.freeze
|
20
|
+
|
21
|
+
def self.[](key)
|
22
|
+
@@config[key]
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.session(sess)
|
26
|
+
@@config[:sessions][sess]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.session_names
|
30
|
+
@@config[:sessions].keys
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.session?(sess)
|
34
|
+
session_names.include?(sess.to_sym)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.add_session(label, hsh)
|
38
|
+
@@config[:sessions][label.to_sym] = hsh
|
39
|
+
File.open(CONFIG_FILE, "w") { |io| io.write @@config.to_yaml }
|
40
|
+
end
|
41
|
+
|
42
|
+
if File.exist?(CONFIG_FILE)
|
43
|
+
user_config = YAML.load_file(CONFIG_FILE)
|
44
|
+
else
|
45
|
+
user_config = DEFAULT_CONFIG
|
46
|
+
FileUtils.mkdir_p CONFIG_DIR
|
47
|
+
File.open(CONFIG_FILE, "w") { |io| io.write DEFAULT_CONFIG.to_yaml }
|
48
|
+
end
|
49
|
+
|
50
|
+
@@config = DEFAULT_CONFIG.merge(user_config)
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
require_relative "record/abstract"
|
4
|
+
require_relative "record/login"
|
5
|
+
require_relative "record/environment"
|
6
|
+
require_relative "record/unstructured"
|
7
|
+
|
8
|
+
module KBSecret
|
9
|
+
module Record
|
10
|
+
def self.record_classes
|
11
|
+
klasses = constants.map(&Record.method(:const_get)).grep(Class)
|
12
|
+
klasses.delete(Record::Abstract)
|
13
|
+
klasses
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.record_types
|
17
|
+
record_classes.map(&:type)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.type?(type)
|
21
|
+
record_types.include?(type)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load_record!(session, path)
|
25
|
+
hsh = JSON.parse(File.read(path), symbolize_names: true)
|
26
|
+
klass = record_classes.find { |c| c.type == hsh[:type] }
|
27
|
+
klass.load!(session, hsh)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module KBSecret
|
4
|
+
module Record
|
5
|
+
# @abstract
|
6
|
+
class Abstract
|
7
|
+
attr_accessor :session
|
8
|
+
attr_reader :timestamp
|
9
|
+
attr_reader :label
|
10
|
+
attr_reader :type
|
11
|
+
attr_reader :data
|
12
|
+
|
13
|
+
def self.type
|
14
|
+
name.split("::").last.downcase
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.load!(session, hsh)
|
18
|
+
instance = allocate
|
19
|
+
instance.session = session
|
20
|
+
instance.initialize_from_hash(hsh)
|
21
|
+
|
22
|
+
instance
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(session, label)
|
26
|
+
@session = session
|
27
|
+
@timestamp = Time.now.to_i
|
28
|
+
@label = label
|
29
|
+
@type = self.class.type
|
30
|
+
@data = {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize_from_hash(hsh)
|
34
|
+
@timestamp = hsh[:timestamp]
|
35
|
+
@label = hsh[:label]
|
36
|
+
@type = hsh[:type]
|
37
|
+
@data = hsh[:data]
|
38
|
+
end
|
39
|
+
|
40
|
+
def path
|
41
|
+
File.join(session.directory, "#{label}.json")
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h
|
45
|
+
{
|
46
|
+
timestamp: timestamp,
|
47
|
+
label: label,
|
48
|
+
type: type,
|
49
|
+
data: data,
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
def sync!
|
54
|
+
# bump the timestamp every time we sync
|
55
|
+
@timestamp = Time.now.to_i
|
56
|
+
|
57
|
+
File.write(path, JSON.pretty_generate(to_h))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module KBSecret
|
2
|
+
module Record
|
3
|
+
class Environment < Abstract
|
4
|
+
def initialize(session, label, variable, value)
|
5
|
+
super(session, label)
|
6
|
+
|
7
|
+
@data = {
|
8
|
+
environment: {
|
9
|
+
variable: variable,
|
10
|
+
value: value,
|
11
|
+
},
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def variable
|
16
|
+
@data[:environment][:variable]
|
17
|
+
end
|
18
|
+
|
19
|
+
def variable=(var)
|
20
|
+
@data[:environment][:variable] = var
|
21
|
+
sync!
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
@data[:environment][:value]
|
26
|
+
end
|
27
|
+
|
28
|
+
def value=(val)
|
29
|
+
@data[:environment][:value] = val
|
30
|
+
sync!
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_assignment
|
34
|
+
"#{variable}='#{value}'"
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_export
|
38
|
+
"export #{to_assignment}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module KBSecret
|
2
|
+
module Record
|
3
|
+
class Login < Abstract
|
4
|
+
def initialize(session, label, user, pass)
|
5
|
+
super(session, label)
|
6
|
+
|
7
|
+
@data = {
|
8
|
+
login: {
|
9
|
+
username: user,
|
10
|
+
password: pass,
|
11
|
+
},
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def username
|
16
|
+
@data[:login][:username]
|
17
|
+
end
|
18
|
+
|
19
|
+
def username=(user)
|
20
|
+
@data[:login][:username] = user
|
21
|
+
sync!
|
22
|
+
end
|
23
|
+
|
24
|
+
def password
|
25
|
+
@data[:login][:password]
|
26
|
+
end
|
27
|
+
|
28
|
+
def password=(pass)
|
29
|
+
@data[:login][:password] = pass
|
30
|
+
sync!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module KBSecret
|
2
|
+
module Record
|
3
|
+
class Unstructured < Abstract
|
4
|
+
def initialize(session, label, text)
|
5
|
+
super(session, label)
|
6
|
+
|
7
|
+
@data = {
|
8
|
+
text: text
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def text
|
13
|
+
@data[:text]
|
14
|
+
end
|
15
|
+
|
16
|
+
def text=(new_text)
|
17
|
+
@data[:text] = new_text
|
18
|
+
sync!
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module KBSecret
|
2
|
+
class Session
|
3
|
+
attr_reader :label
|
4
|
+
attr_reader :config
|
5
|
+
attr_reader :directory
|
6
|
+
attr_reader :records
|
7
|
+
|
8
|
+
def initialize(label: :default)
|
9
|
+
@label = label
|
10
|
+
@config = Config.session(label.to_sym)
|
11
|
+
|
12
|
+
@directory = rel_path config[:root], mkdir: true
|
13
|
+
@records = load_records!
|
14
|
+
end
|
15
|
+
|
16
|
+
def record_labels
|
17
|
+
records.map(&:label)
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_record(type, label, *args)
|
21
|
+
klass = Record.record_classes.find { |k| k.type == type }
|
22
|
+
arity = klass.instance_method(:initialize).arity - 2
|
23
|
+
|
24
|
+
unless arity == args.size
|
25
|
+
raise RecordCreationArityError.new(arity, args.size)
|
26
|
+
end
|
27
|
+
|
28
|
+
record = klass.new(self, label, *args)
|
29
|
+
records << record
|
30
|
+
record.sync!
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_record(rec_label)
|
34
|
+
record = records.find { |r| r.label == rec_label }
|
35
|
+
return unless record
|
36
|
+
|
37
|
+
File.delete(record.path)
|
38
|
+
records.delete(record)
|
39
|
+
end
|
40
|
+
|
41
|
+
def record?(label)
|
42
|
+
record_labels.include?(label)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def record_paths
|
48
|
+
Dir[File.join(directory, "*.json")]
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_records!
|
52
|
+
record_paths.map do |path|
|
53
|
+
Record.load_record! self, path
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def stringified_users
|
58
|
+
config[:users].join(",")
|
59
|
+
end
|
60
|
+
|
61
|
+
def rel_path(rel, mkdir: false)
|
62
|
+
path = File.join(Config[:mount], "private", stringified_users, rel)
|
63
|
+
|
64
|
+
FileUtils.mkdir_p path if mkdir
|
65
|
+
|
66
|
+
path
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kbsecret
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- William Woodruff
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-02-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: keybase-unofficial
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: slop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.4'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.4'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: tty-prompt
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.10.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.10.0
|
55
|
+
description: Manages your passwords, environment, and more via KBFS.
|
56
|
+
email: william@tuffbizz.com
|
57
|
+
executables:
|
58
|
+
- kbsecret
|
59
|
+
- kbsecret-rm
|
60
|
+
- kbsecret-login
|
61
|
+
- kbsecret-new
|
62
|
+
- kbsecret-env
|
63
|
+
- kbsecret-new-session
|
64
|
+
- kbsecret-list
|
65
|
+
- kbsecret-sessions
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- ".yardopts"
|
70
|
+
- LICENSE
|
71
|
+
- README.md
|
72
|
+
- bin/kbsecret
|
73
|
+
- bin/kbsecret-env
|
74
|
+
- bin/kbsecret-list
|
75
|
+
- bin/kbsecret-login
|
76
|
+
- bin/kbsecret-new
|
77
|
+
- bin/kbsecret-new-session
|
78
|
+
- bin/kbsecret-rm
|
79
|
+
- bin/kbsecret-sessions
|
80
|
+
- lib/kbsecret.rb
|
81
|
+
- lib/kbsecret/config.rb
|
82
|
+
- lib/kbsecret/exceptions.rb
|
83
|
+
- lib/kbsecret/record.rb
|
84
|
+
- lib/kbsecret/record/abstract.rb
|
85
|
+
- lib/kbsecret/record/environment.rb
|
86
|
+
- lib/kbsecret/record/login.rb
|
87
|
+
- lib/kbsecret/record/unstructured.rb
|
88
|
+
- lib/kbsecret/session.rb
|
89
|
+
homepage: https://github.com/woodruffw/kbsecret
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata: {}
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.3.0
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
requirements: []
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 2.6.8
|
110
|
+
signing_key:
|
111
|
+
specification_version: 4
|
112
|
+
summary: kbsecret - A KBFS (Keybase) backed secret manager.
|
113
|
+
test_files: []
|