kbsecret 0.9.5 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a93f10c5ff4add094ea83dc950239250ffda4e31
4
- data.tar.gz: 3d8ddfe46e26ed3751d541b762c56804fb194c35
3
+ metadata.gz: 95af6343b31d058c770ddc7c9ee0a82c3a5f984c
4
+ data.tar.gz: 6d07e7ea5d2a10b0d2f3a46979d447c3785642ed
5
5
  SHA512:
6
- metadata.gz: d92789c5a47dfccb4422545b9bdf5c7bfc3d086599783336c982cc02a2e37749bab41e9cfc5579db9a3451b726683e044d3efaa302eefd3e5e79d2037af31719
7
- data.tar.gz: 2fc805de715e71bbfc577f9f1258b9b2b46d5cad421126173afd6d8d3864b386e8da035c6b3e99caaa5e361f86d49abfec5e4a93a982dad050ce9e5b0318d5be
6
+ metadata.gz: b60c937f2a8bf3c9271299f602727059af992b0bc2510040ce057fbcd08beae232af1c84d8fa1def9a0e32c362511ec7e6906772a655ed1e8c756f1c83025ba2
7
+ data.tar.gz: 1f11837a4cdd43d7f4439a48544bbf54a7e46715ef069495cf106469bebb65634d5948ee2b370d7d9c75bae960506c2482656321b96031642d8549122db26095
data/lib/kbsecret/cli.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "colored2"
4
3
  require "slop"
5
4
  require "dreck"
6
5
  require "abbrev"
6
+ require "pastel"
7
7
 
8
8
  module KBSecret
9
9
  # An encapsulation of useful methods for kbsecret's CLI.
@@ -13,6 +13,9 @@ module KBSecret
13
13
  # Abbreviations for record types (e.g., `env` for `environment`).
14
14
  TYPE_ALIASES = Hash.new { |_, k| k }.update(Abbrev.abbrev(Record.record_types)).freeze
15
15
 
16
+ # ANSI color objects for prettifying output.
17
+ GREEN, YELLOW, RED = ->(p) { [p.green, p.yellow, p.red].map(&:detach) }.call(Pastel.new)
18
+
16
19
  # @return [Slop::Result, nil] the result of option parsing, if requested
17
20
  # via {#slop}
18
21
  attr_reader :opts
@@ -155,7 +158,7 @@ module KBSecret
155
158
  # @return [void]
156
159
  def info(msg)
157
160
  return unless @opts.verbose?
158
- STDERR.puts "#{"Info".green}: #{msg}"
161
+ STDERR.puts "#{GREEN["Info"]}: #{msg}"
159
162
  end
160
163
 
161
164
  # Print an informational message via {#info} and exit successfully.
@@ -172,7 +175,7 @@ module KBSecret
172
175
  # @return [void]
173
176
  def warn(msg)
174
177
  return if @opts.no_warn?
175
- STDERR.puts "#{"Warning".yellow}: #{msg}"
178
+ STDERR.puts "#{YELLOW["Warning"]}: #{msg}"
176
179
  end
177
180
 
178
181
  # Print an error message and terminate.
@@ -180,7 +183,7 @@ module KBSecret
180
183
  # @return [void]
181
184
  # @note This method does not return!
182
185
  def die(msg)
183
- pretty = "#{"Fatal".red}: #{msg}"
186
+ pretty = "#{RED["Fatal"]}: #{msg}"
184
187
  abort pretty
185
188
  end
186
189
 
@@ -190,7 +193,7 @@ module KBSecret
190
193
  # @return [void]
191
194
  # @note This method does not return!
192
195
  def die(msg)
193
- pretty = "#{"Fatal".red}: #{msg}"
196
+ pretty = "#{RED["Fatal"]}: #{msg}"
194
197
  abort pretty
195
198
  end
196
199
 
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "kbsecret"
5
+ include KBSecret
6
+
7
+ cmd = CLI.create do |c|
8
+ c.slop do |o|
9
+ o.banner = <<~HELP
10
+ Usage:
11
+ kbsecret cp [options] <source> <destination> <record [record ...]>
12
+ HELP
13
+
14
+ o.bool "-f", "--force", "force copying (ignore overwrites)"
15
+ o.bool "-m", "--move", "delete the record after copying"
16
+ end
17
+
18
+ c.dreck do
19
+ string :src_sess
20
+ string :dst_sess
21
+ list :string, :labels
22
+ end
23
+ end
24
+
25
+ cmd.guard do
26
+ src_sess = Session.new label: cmd.args[:src_sess]
27
+ dst_sess = Session.new label: cmd.args[:dst_sess]
28
+
29
+ selected_records = src_sess.records.select { |r| cmd.args[:labels].include?(r.label) }
30
+ cmd.die "No such record(s)." if selected_records.empty?
31
+
32
+ overlaps = dst_sess.record_labels & selected_records.map(&:label)
33
+
34
+ # the code below actually handles the overwriting if necessary, but we fail early here for
35
+ # friendliness and to avoid half-copying the selected records
36
+ unless overlaps.empty? || cmd.opts.force?
37
+ cmd.die "Refusing to overwrite existing record(s) without --force."
38
+ end
39
+
40
+ selected_records.each do |record|
41
+ dst_sess.import_record(record, overwrite: cmd.opts.force?)
42
+ src_sess.delete_record(label) if cmd.opts.move?
43
+ end
44
+ end
@@ -38,6 +38,8 @@ type = cmd.args[:type]
38
38
  label = cmd.args[:label]
39
39
  resolved_type = CLI::TYPE_ALIASES[type]
40
40
 
41
+ # the code below actually handles the overwriting if necessary, but we fail early here
42
+ # for friendliness and to avoid prompting the user for input unnecessarily
41
43
  if cmd.session.record?(label) && !cmd.opts.force?
42
44
  cmd.die "Refusing to overwrite an existing record without --force."
43
45
  end
@@ -61,4 +63,6 @@ fields = if cmd.opts.terse?
61
63
  end
62
64
  end
63
65
 
64
- cmd.guard { cmd.session.add_record(resolved_type, label, *fields) }
66
+ cmd.guard do
67
+ cmd.session.add_record resolved_type, label, *fields, overwrite: cmd.opts.force?
68
+ end
@@ -11,9 +11,9 @@ def new_session(label, cmd)
11
11
  end
12
12
 
13
13
  if cmd.opts[:team]
14
- teams = Keybase::Local::Team.list_memberships["teams"]
14
+ teams = Keybase::Local::Team.list_self_memberships.teams
15
15
 
16
- unless teams.any? { |t| t["fq_name"] == cmd.opts[:team] }
16
+ unless teams.any? { |t| t.fq_name == cmd.opts[:team] }
17
17
  cmd.die "No such team (either nonexistent or non-member)."
18
18
  end
19
19
 
@@ -9,6 +9,7 @@ module KBSecret
9
9
 
10
10
  # Raised during record loading if a particular file can't be loaded
11
11
  class RecordLoadError < KBSecretError
12
+ # @param path [String] the path to the record
12
13
  def initialize(path)
13
14
  base = File.basename(path)
14
15
  super "Failed to load record in file: '#{base}'"
@@ -17,6 +18,7 @@ module KBSecret
17
18
 
18
19
  # Raised during record creation if an unknown record type is requested.
19
20
  class RecordTypeUnknownError < KBSecretError
21
+ # @param type [String, Symbol] the record type
20
22
  def initialize(type)
21
23
  super "Unknown record type: '#{type}'"
22
24
  end
@@ -24,13 +26,25 @@ module KBSecret
24
26
 
25
27
  # Raised during record creation if too many/few arguments are given.
26
28
  class RecordCreationArityError < KBSecretError
29
+ # @param exp [Integer] the number of expected arguments
30
+ # @param act [Integer] the number of actual arguments
27
31
  def initialize(exp, act)
28
32
  super "Needed #{exp} arguments for this record, got #{act}"
29
33
  end
30
34
  end
31
35
 
36
+ # Raised when record creation or import would cause an unintended overwrite.
37
+ class RecordOverwriteError < KBSecretError
38
+ # @param session [Session] the session being modified
39
+ # @param label [String] the label being overwritten in the session
40
+ def initialize(session, label)
41
+ super "Record '#{label}' already exists in '#{session.label}'"
42
+ end
43
+ end
44
+
32
45
  # Raised during session load if an error occurs.
33
46
  class SessionLoadError < KBSecretError
47
+ # @param msg [String] the error message
34
48
  def initialize(msg)
35
49
  super "Session loading failure: #{msg}"
36
50
  end
@@ -38,13 +52,23 @@ module KBSecret
38
52
 
39
53
  # Raised during session lookup if an unknown session is requested.
40
54
  class SessionUnknownError < KBSecretError
55
+ # @param sess [String, Symbol] the label of the session
41
56
  def initialize(sess)
42
57
  super "Unknown session: '#{sess}'"
43
58
  end
44
59
  end
45
60
 
61
+ # Raised during record import if the source is the same as the destination.
62
+ class SessionImportError < KBSecretError
63
+ # @param session [Session] the session being imported into
64
+ def initialize(session)
65
+ super "Session '#{session.label}' cannot import records from itself"
66
+ end
67
+ end
68
+
46
69
  # Raised during generator lookup if an unknown profile is requested.
47
70
  class GeneratorUnknownError < KBSecretError
71
+ # @param gen [String, Symbol] the label of the generator
48
72
  def initialize(gen)
49
73
  super "Unknown generator profile: '#{gen}'"
50
74
  end
@@ -52,6 +76,7 @@ module KBSecret
52
76
 
53
77
  # Raised during generator creation if an unknown generator format is requested.
54
78
  class GeneratorFormatError < KBSecretError
79
+ # @param fmt [String, Symbol] the format of the generator
55
80
  def initialize(fmt)
56
81
  super "Unknown generator format: '#{fmt}'"
57
82
  end
@@ -59,6 +84,7 @@ module KBSecret
59
84
 
60
85
  # Raised during generator creation if a non-positive generator length is requested.
61
86
  class GeneratorLengthError < KBSecretError
87
+ # @param length [Integer] the length of the generator
62
88
  def initialize(length)
63
89
  super "Bad secret generator length (#{length}, must be positive)"
64
90
  end
@@ -62,17 +62,25 @@ module KBSecret
62
62
  # @param type [String, Symbol] the type of record (see {Record.record_types})
63
63
  # @param label [String, Symbol] the new record's label
64
64
  # @param args [Array<String>] the record-type specific arguments
65
+ # @param overwrite [Boolean] whether or not to overwrite an existing record if necessary
65
66
  # @return [void]
66
67
  # @raise [Exceptions::UnknownRecordTypeError] if the requested type does not exist
67
68
  # in {Record.record_types}
68
69
  # @raise [Exceptions::RecordCreationArityError] if the number of specified record
69
70
  # arguments does not match the record type's constructor
70
- def add_record(type, label, *args)
71
+ # @raise [Exceptions::RecordOverwriteError] if the record addition would cause an
72
+ # unchecked overwrite
73
+ def add_record(type, label, *args, overwrite: false)
71
74
  klass = Record.class_for(type.to_sym)
72
75
  arity = klass.external_fields.length
73
76
 
74
77
  raise Exceptions::RecordCreationArityError.new(arity, args.size) unless arity == args.size
75
78
 
79
+ if record? label
80
+ raise Exceptions::RecordOverwriteError.new(self, label) unless overwrite
81
+ delete_record label
82
+ end
83
+
76
84
  body = klass.external_fields.zip(args).to_h
77
85
  record = klass.new(self, label.to_s, **body)
78
86
 
@@ -80,6 +88,26 @@ module KBSecret
80
88
  record.sync!
81
89
  end
82
90
 
91
+ # Import an existing record from another session.
92
+ # @param record [Record] the record to import
93
+ # @param overwrite [Boolean] whether or not to overwrite an existing record if necessary
94
+ # @return [void]
95
+ # @raise [Exceptions::SessionImportError] if the record's source session is our session
96
+ # @raise [Exceptions::RecordOverwriteError] if record import would cause an unchecked overwrite
97
+ def import_record(record, overwrite: false)
98
+ raise Exceptions::SessionImportError, self if self == record.session
99
+
100
+ if record? record.label
101
+ raise Exceptions::RecordOverwriteError.new(self, record.label) unless overwrite
102
+ delete_record record.label
103
+ end
104
+
105
+ klass = record.class
106
+ imported_record = klass.load!(self, record.to_h)
107
+ records << imported_record
108
+ imported_record.sync!
109
+ end
110
+
83
111
  # Delete a record from the session, if it exists. Does nothing if
84
112
  # no such record can be found.
85
113
  # @param label [String, Symbol] the label of the record to delete
@@ -113,6 +141,15 @@ module KBSecret
113
141
  Dir[File.join(path, "*.json")]
114
142
  end
115
143
 
144
+ # Compare two sessions for equality.
145
+ # @param other [Session] the other object to compare against
146
+ # @return [Boolean] whether or not the two sessions are equal
147
+ # @note The equality of two sessions is determined *solely* by them having the same
148
+ # session directory, *not* by having the same label *or* the same in-memory state.
149
+ def ==(other)
150
+ other.class == self.class && other.path == path
151
+ end
152
+
116
153
  # Load all records associated with the session.
117
154
  # @return [Array<Record::Abstract>] all records in the session
118
155
  # @api private
data/lib/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module KBSecret
4
4
  # kbsecret's current version
5
- VERSION = "0.9.5"
5
+ VERSION = "0.9.6"
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kbsecret
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Woodruff
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-30 00:00:00.000000000 Z
11
+ date: 2017-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aruba
@@ -137,61 +137,61 @@ dependencies:
137
137
  - !ruby/object:Gem::Version
138
138
  version: '1.1'
139
139
  - !ruby/object:Gem::Dependency
140
- name: colored2
140
+ name: dreck
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '3.1'
145
+ version: 0.2.1
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '3.1'
152
+ version: 0.2.1
153
153
  - !ruby/object:Gem::Dependency
154
- name: dreck
154
+ name: inih
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: 0.2.1
159
+ version: '1.1'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: 0.2.1
166
+ version: '1.1'
167
167
  - !ruby/object:Gem::Dependency
168
- name: inih
168
+ name: keybase-unofficial
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: '1.1'
173
+ version: '1.0'
174
174
  type: :runtime
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: '1.1'
180
+ version: '1.0'
181
181
  - !ruby/object:Gem::Dependency
182
- name: keybase-unofficial
182
+ name: pastel
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '0.9'
187
+ version: '0.7'
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - "~>"
193
193
  - !ruby/object:Gem::Version
194
- version: '0.9'
194
+ version: '0.7'
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: slop
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -233,6 +233,7 @@ files:
233
233
  - bin/kbsecret
234
234
  - lib/kbsecret.rb
235
235
  - lib/kbsecret/cli.rb
236
+ - lib/kbsecret/cli/kbsecret-cp
236
237
  - lib/kbsecret/cli/kbsecret-dump-fields
237
238
  - lib/kbsecret/cli/kbsecret-env
238
239
  - lib/kbsecret/cli/kbsecret-generator