kbsecret 0.0.1 → 0.0.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9587292e2be80b4a53d26f4bdda06e2a94d1b7be
4
- data.tar.gz: 5dd01f1c1819f0f17537487ff73e621e27193938
3
+ metadata.gz: f468bb679d062bf75ce1d379e4cd76c4544c090f
4
+ data.tar.gz: d862b1c03920b88de8942fac32ad6f7b4fb89bd2
5
5
  SHA512:
6
- metadata.gz: 4227a95d277ed064a5cd1b4dd3add4dedcabb97d041c66c5dffec753a87ab0de73684582a4d18e34bd33c192912d54dc173527516daa10a172dbd19d55e73574
7
- data.tar.gz: b14e1d1524a71d70e85185c7c61c43a3791923c5698273b68398b6dcf1d072a682a3f7bfb695666d1cd25819812d740e5cf150c71142571d741dd5e426091845
6
+ metadata.gz: a9de681a6224e5f577deb31e8a72cafe288fa9d48c7de5e7438616c8887408a3705e813bd588fdfc328b84683e0f5b51540f58911163c81494cde918851306a4
7
+ data.tar.gz: 0fbe3224bc94a6a39eea6e1e0c578ab01fcd4755e92e827cd08441151086bd1cc2c65d00c2de8cfbefca0a32847001d53d7fc5496058de45bdf937cc7e3e64b7
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  kbsecret
2
2
  ========
3
3
 
4
+ *Note*: This is still a work in process. Use it with caution.
5
+
4
6
  kbsecret is a combined library/utility that provides a secret management
5
7
  interface for [KBFS](https://keybase.io/docs/kbfs) and
6
8
  [Keybase](https://keybase.io/).
@@ -16,7 +18,7 @@ interface for [KBFS](https://keybase.io/docs/kbfs) and
16
18
 
17
19
  ### Installation
18
20
 
19
- kbsecret is available via RubyGems:
21
+ kbsecret is available via [RubyGems](https://rubygems.org/gems/kbsecret):
20
22
 
21
23
  ```bash
22
24
  $ gem install kbsecret
@@ -26,7 +28,7 @@ For hacking:
26
28
 
27
29
  ```bash
28
30
  $ git clone git@github.com:woodruffw/kbsecret.git && cd kbsecret
29
- $ RUBYLIB=./lib PATH=${PATH}:./bin ./bin/kbsecret help
31
+ $ RUBYLIB=./lib PATH=./bin:${PATH} ./bin/kbsecret help
30
32
  ```
31
33
 
32
34
  ### Usage
@@ -2,11 +2,18 @@ require "yaml"
2
2
  require "fileutils"
3
3
 
4
4
  module KBSecret
5
+ # Global and per-session configuration for kbsecret.
5
6
  class Config
7
+ # the configuration directory
8
+ # @api private
6
9
  CONFIG_DIR = File.expand_path("~/.config/kbsecret").freeze
7
10
 
11
+ # the configuration file
12
+ # @api private
8
13
  CONFIG_FILE = File.join(CONFIG_DIR, "config.yml").freeze
9
14
 
15
+ # configuration defaults
16
+ # @api private
10
17
  DEFAULT_CONFIG = {
11
18
  mount: "/keybase",
12
19
 
@@ -18,22 +25,36 @@ module KBSecret
18
25
  }
19
26
  }.freeze
20
27
 
28
+ # Retrieve a configured value.
29
+ # @param key [String]
30
+ # @return [Object] the corresponding configuration
21
31
  def self.[](key)
22
32
  @@config[key]
23
33
  end
24
34
 
35
+ # Retrieve a session's configuration.
36
+ # @param sess [Symbol] the session's label
37
+ # @return [Hash] the session configuration
25
38
  def self.session(sess)
26
39
  @@config[:sessions][sess]
27
40
  end
28
41
 
42
+ # @return [Array<Symbol>] all configured session labels
43
+ # @todo This should be session_labels.
29
44
  def self.session_names
30
45
  @@config[:sessions].keys
31
46
  end
32
47
 
48
+ # @param sess [Symbol] the session label
49
+ # @return [Boolean] whether or not the given session is configured
33
50
  def self.session?(sess)
34
51
  session_names.include?(sess.to_sym)
35
52
  end
36
53
 
54
+ # Configure a session.
55
+ # @param label [Symbol] the session label
56
+ # @param hsh [Hash] the session configuration
57
+ # @todo This should be configure_session.
37
58
  def self.add_session(label, hsh)
38
59
  @@config[:sessions][label.to_sym] = hsh
39
60
  File.open(CONFIG_FILE, "w") { |io| io.write @@config.to_yaml }
@@ -1,7 +1,9 @@
1
1
  module KBSecret
2
+ # A generic error in kbsecret.
2
3
  class KBSecretError < RuntimeError
3
4
  end
4
5
 
6
+ # Raised during record creation if too many/few arguments are given.
5
7
  class RecordCreationArityError < RuntimeError
6
8
  def initialize(exp, act)
7
9
  super "Needed #{exp} arguments for this record, got #{act}"
@@ -2,6 +2,8 @@ require "json"
2
2
 
3
3
  module KBSecret
4
4
  module Record
5
+ # Represents an abstract kbsecret record that can be subclassed to produce
6
+ # more useful records.
5
7
  # @abstract
6
8
  class Abstract
7
9
  attr_accessor :session
@@ -10,10 +12,18 @@ module KBSecret
10
12
  attr_reader :type
11
13
  attr_reader :data
12
14
 
15
+ # @return [String] the record's type
16
+ # @example
17
+ # KBSecret::Record::Abstract.type # => "abstract"
13
18
  def self.type
14
19
  name.split("::").last.downcase
15
20
  end
16
21
 
22
+ # Load the given hash-representation into a record.
23
+ # @param session [Session] the session to associate with
24
+ # @param hsh [Hash] the record's hash-representation
25
+ # @return [Record::AbstractRecord] the created record
26
+ # @api private
17
27
  def self.load!(session, hsh)
18
28
  instance = allocate
19
29
  instance.session = session
@@ -22,6 +32,10 @@ module KBSecret
22
32
  instance
23
33
  end
24
34
 
35
+ # Create a brand new record, associated with a session.
36
+ # @param session [Session] the session to associate with
37
+ # @param label [Symbol] the new record's label
38
+ # @note Creation does *not* sync the new record; see {#sync!} for that.
25
39
  def initialize(session, label)
26
40
  @session = session
27
41
  @timestamp = Time.now.to_i
@@ -30,6 +44,10 @@ module KBSecret
30
44
  @data = {}
31
45
  end
32
46
 
47
+ # Fill in instance fields from a record's hash-representation.
48
+ # @param hsh [Hash] the record's hash-representation.
49
+ # @return [void]
50
+ # @api private
33
51
  def initialize_from_hash(hsh)
34
52
  @timestamp = hsh[:timestamp]
35
53
  @label = hsh[:label]
@@ -37,10 +55,16 @@ module KBSecret
37
55
  @data = hsh[:data]
38
56
  end
39
57
 
58
+ # The fully qualified path to the record's file.
59
+ # @return [String] the path
60
+ # @note If the record hasn't been synced to disk, this path may not
61
+ # exist yet.
40
62
  def path
41
63
  File.join(session.directory, "#{label}.json")
42
64
  end
43
65
 
66
+ # Create a hash-representation of the current record.
67
+ # @return [Hash] the hash-representation
44
68
  def to_h
45
69
  {
46
70
  timestamp: timestamp,
@@ -50,6 +74,9 @@ module KBSecret
50
74
  }
51
75
  end
52
76
 
77
+ # Write the record's in-memory state to disk.
78
+ # @note Every sync updates the record's timestamp.
79
+ # @return [void]
53
80
  def sync!
54
81
  # bump the timestamp every time we sync
55
82
  @timestamp = Time.now.to_i
@@ -1,9 +1,19 @@
1
+ require "shellwords"
2
+
1
3
  module KBSecret
2
4
  module Record
5
+ # Represents a record containing an environment variable and value.
3
6
  class Environment < Abstract
7
+ # @param session [Session] the session to associate with
8
+ # @param label [Symbol] the new record's label
9
+ # @param variable [String] the new record's variable
10
+ # @param value [String] the new record's value
4
11
  def initialize(session, label, variable, value)
5
12
  super(session, label)
6
13
 
14
+ value = value.shellescape
15
+ variable = variable.shellescape
16
+
7
17
  @data = {
8
18
  environment: {
9
19
  variable: variable,
@@ -12,28 +22,38 @@ module KBSecret
12
22
  }
13
23
  end
14
24
 
25
+ # @return [String] the record's variable
15
26
  def variable
16
27
  @data[:environment][:variable]
17
28
  end
18
29
 
30
+ # @param var [String] the new variable to insert into the record
31
+ # @return [void]
32
+ # @note Triggers a record sync; see {Abstract#sync!}.
19
33
  def variable=(var)
20
34
  @data[:environment][:variable] = var
21
35
  sync!
22
36
  end
23
37
 
38
+ # @return [String] the record's value
24
39
  def value
25
40
  @data[:environment][:value]
26
41
  end
27
42
 
43
+ # @param val [String] the new value to insert into the record
44
+ # @return [void]
45
+ # @note Triggers a record sync; see {Abstract#sync!}.
28
46
  def value=(val)
29
47
  @data[:environment][:value] = val
30
48
  sync!
31
49
  end
32
50
 
51
+ # @return [String] a sh-style environment assignment
33
52
  def to_assignment
34
- "#{variable}='#{value}'"
53
+ "#{variable}=#{value}"
35
54
  end
36
55
 
56
+ # @return [String] a sh-style environment export line
37
57
  def to_export
38
58
  "export #{to_assignment}"
39
59
  end
@@ -1,6 +1,11 @@
1
1
  module KBSecret
2
2
  module Record
3
+ # Represents a record containing a login (username, password) pair.
3
4
  class Login < Abstract
5
+ # @param session [Session] the session to associate with
6
+ # @param label [Symbol] the new record's label
7
+ # @param user [String] the new record's username
8
+ # @param pass [String] the new record's password
4
9
  def initialize(session, label, user, pass)
5
10
  super(session, label)
6
11
 
@@ -12,19 +17,27 @@ module KBSecret
12
17
  }
13
18
  end
14
19
 
20
+ # @return [String] the record's username
15
21
  def username
16
22
  @data[:login][:username]
17
23
  end
18
24
 
25
+ # @param user [String] the new username to insert into the record
26
+ # @return [void]
27
+ # @note Triggers a record sync; see {Abstract#sync!}.
19
28
  def username=(user)
20
29
  @data[:login][:username] = user
21
30
  sync!
22
31
  end
23
32
 
33
+ # @return [String] the record's password
24
34
  def password
25
35
  @data[:login][:password]
26
36
  end
27
37
 
38
+ # @param pass [String] the new password to insert into the record
39
+ # @return [void]
40
+ # @note Triggers a record sync; see {Abstract#sync!}.
28
41
  def password=(pass)
29
42
  @data[:login][:password] = pass
30
43
  sync!
@@ -1,6 +1,10 @@
1
1
  module KBSecret
2
2
  module Record
3
+ # Represents a record containing unstructured text.
3
4
  class Unstructured < Abstract
5
+ # @param session [Session] the session to associate with
6
+ # @param label [Symbol] the new record's label
7
+ # @param text [String] the new record's unstructured text
4
8
  def initialize(session, label, text)
5
9
  super(session, label)
6
10
 
@@ -9,10 +13,14 @@ module KBSecret
9
13
  }
10
14
  end
11
15
 
16
+ # @return [String] the record's unstructured text
12
17
  def text
13
18
  @data[:text]
14
19
  end
15
20
 
21
+ # @param new_text [String] the new text to insert into the record
22
+ # @return [void]
23
+ # @note Triggers a record sync; see {Abstract#sync!}.
16
24
  def text=(new_text)
17
25
  @data[:text] = new_text
18
26
  sync!
@@ -6,21 +6,30 @@ require_relative "record/environment"
6
6
  require_relative "record/unstructured"
7
7
 
8
8
  module KBSecret
9
+ # The namespace for kbsecret records types.
9
10
  module Record
11
+ # @return [Array<Class>] the class objects of all non-abstract record types
10
12
  def self.record_classes
11
13
  klasses = constants.map(&Record.method(:const_get)).grep(Class)
12
14
  klasses.delete(Record::Abstract)
13
15
  klasses
14
16
  end
15
17
 
18
+ # @return [Array<String>] the types of all records
16
19
  def self.record_types
17
20
  record_classes.map(&:type)
18
21
  end
19
22
 
23
+ # @return [Boolean] whether a record class exists of the given type
20
24
  def self.type?(type)
21
25
  record_types.include?(type)
22
26
  end
23
27
 
28
+ # Load a record by path into the given session.
29
+ # @param session [Session] the session to load into
30
+ # @param path [String] the fully-qualified record path
31
+ # @return [Record::AbstractRecord] the loaded record
32
+ # @api private
24
33
  def self.load_record!(session, path)
25
34
  hsh = JSON.parse(File.read(path), symbolize_names: true)
26
35
  klass = record_classes.find { |c| c.type == hsh[:type] }
@@ -1,10 +1,20 @@
1
1
  module KBSecret
2
+ # Represents a session of N keybase users with collective read/write
3
+ # access to a collection of records.
2
4
  class Session
5
+ # @return [Symbol] the session's label
3
6
  attr_reader :label
7
+
8
+ # @return [Hash] the session-specific configuration, from
9
+ # {Config::CONFIG_FILE}
4
10
  attr_reader :config
5
11
  attr_reader :directory
6
12
  attr_reader :records
7
13
 
14
+ # @param label [Symbol] the label of the session to initialize
15
+ # @note This does not *create* a new session, but loads one already
16
+ # specified in {Config::CONFIG_FILE}. To *create* a new session,
17
+ # see {Config.add_session}.
8
18
  def initialize(label: :default)
9
19
  @label = label
10
20
  @config = Config.session(label.to_sym)
@@ -13,10 +23,20 @@ module KBSecret
13
23
  @records = load_records!
14
24
  end
15
25
 
26
+ # @return [Array<Symbol>] the labels of all records known to the session
27
+ # @example
28
+ # session.record_labels # => [:website1, :apikey1, :website2]
16
29
  def record_labels
17
30
  records.map(&:label)
18
31
  end
19
32
 
33
+ # Add a record to the session.
34
+ # @param type [String] the type of record (see {Record.record_types})
35
+ # @param label [Symbol] the new record's label
36
+ # @param args [Array<String>] the record-type specific arguments
37
+ # @return [void]
38
+ # @raise RecordCreationArityError if the number of specified record
39
+ # arguments does not match the record type's constructor
20
40
  def add_record(type, label, *args)
21
41
  klass = Record.record_classes.find { |k| k.type == type }
22
42
  arity = klass.instance_method(:initialize).arity - 2
@@ -30,6 +50,10 @@ module KBSecret
30
50
  record.sync!
31
51
  end
32
52
 
53
+ # Delete a record from the session, if it exists. Does nothing if
54
+ # no such record can be found.
55
+ # @param rec_label [Symbol] the label of the record to delete
56
+ # @return [void]
33
57
  def delete_record(rec_label)
34
58
  record = records.find { |r| r.label == rec_label }
35
59
  return unless record
@@ -38,6 +62,8 @@ module KBSecret
38
62
  records.delete(record)
39
63
  end
40
64
 
65
+ # @return [Boolean] whether or not the session contains a record with the
66
+ # given label
41
67
  def record?(label)
42
68
  record_labels.include?(label)
43
69
  end
data/lib/kbsecret.rb CHANGED
@@ -5,9 +5,12 @@ require_relative "kbsecret/exceptions"
5
5
  require_relative "kbsecret/record"
6
6
  require_relative "kbsecret/session"
7
7
 
8
+ # The primary namespace for kbsecret.
8
9
  module KBSecret
9
- VERSION = "0.0.1".freeze
10
+ # kbsecret's current version
11
+ VERSION = "0.0.2".freeze
10
12
 
13
+ # fail very early if the user doesn't have keybase and KBFS running
11
14
  raise Keybase::KeybaseNotRunningError unless Keybase.running?
12
15
  raise Keybase::KBFSNotRunningError unless Dir.exist?(Config[:mount])
13
16
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kbsecret
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Woodruff
@@ -55,14 +55,14 @@ dependencies:
55
55
  description: Manages your passwords, environment, and more via KBFS.
56
56
  email: william@tuffbizz.com
57
57
  executables:
58
- - kbsecret
58
+ - kbsecret-sessions
59
+ - kbsecret-new-session
60
+ - kbsecret-new
59
61
  - kbsecret-rm
62
+ - kbsecret-list
60
63
  - kbsecret-login
61
- - kbsecret-new
62
64
  - kbsecret-env
63
- - kbsecret-new-session
64
- - kbsecret-list
65
- - kbsecret-sessions
65
+ - kbsecret
66
66
  extensions: []
67
67
  extra_rdoc_files: []
68
68
  files: