leeloo 0.0.16 → 0.1.0

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
  SHA256:
3
- metadata.gz: 9214d0cb6dfd5872b74cd14707a591f5550b5c80bb7df2492c056009001d9cd8
4
- data.tar.gz: cb5bb412d04bbafe54d043eb9dd272b07b71e11a2c44ff0f42447632a6a156a8
3
+ metadata.gz: 693f067bfe0f4e494ac28768cecf4d17c8ec7eabb9757d593fb2607236154a95
4
+ data.tar.gz: d86983d28080fdbc547b2856f2aa143d8be1846ac34e3a23760e6aab2a4f3ca4
5
5
  SHA512:
6
- metadata.gz: f2ad8d579c173689b65c7216f2705fa82e764846503c6b028019002f117e7caeb0085bbdf75f9e792d8b5d54265e2e226543c4e409c264df46a12f8b3893f315
7
- data.tar.gz: 9d91319a9b71a4786b361e3068aea55c767ddf0acb8e619e7f115b97df4b23fd0859d1cb77d0e68d81215de2fe19a1e49c5273743734755dbae53fa9c94ca3fa
6
+ metadata.gz: daacdabbfaefe03434138c3bd82625938196778e8a3c842492100fce084d6140d7eccf8d1df4e6afb8f0885e34c48c5b67920a44123f6adbe0c4b88e4f0b12d9
7
+ data.tar.gz: 5c8d5ad16d49c15825a8eab81358286391c57c6b34e31f206c6dc56a2afec52f883baf57e2c52c54e53244913182be0184dda6c212b2c5f672ab57b9fe8a70e3
@@ -1,12 +1,12 @@
1
1
  require 'leeloo/version'
2
2
  require 'leeloo/command'
3
- require 'leeloo/secret'
3
+ require 'leeloo/preferences'
4
4
  require 'leeloo/keystore'
5
- require 'leeloo/config'
5
+ require 'leeloo/secret'
6
+ require 'leeloo/output'
6
7
 
7
8
  module Leeloo
8
9
  def self.start
9
- Config.load
10
10
  Command.new.run
11
11
  end
12
12
  end
@@ -1,7 +1,5 @@
1
1
  require 'commander/import'
2
2
  require 'securerandom'
3
- require 'clipboard'
4
-
5
3
 
6
4
  class String
7
5
  def truncate(max)
@@ -11,9 +9,29 @@ end
11
9
 
12
10
  module Leeloo
13
11
 
12
+ class OutputFactory
13
+ def self.create options
14
+ output = nil
15
+ if options.ascii
16
+ output = Ascii.new
17
+ else
18
+ output = Terminal.new
19
+ end
20
+ if options.clipboard
21
+ ClipboardOutputDecorator.new output
22
+ else
23
+ output
24
+ end
25
+ end
26
+ end
27
+
14
28
  class Command
15
29
  include Commander::Methods
16
30
 
31
+ def initialize
32
+ @preferences = PrivateLocalFileSystemPreferences.new.load
33
+ end
34
+
17
35
  def run
18
36
  program :name, 'leeloo'
19
37
  program :version, Leeloo::VERSION
@@ -27,106 +45,73 @@ module Leeloo
27
45
 
28
46
  default_command :"list"
29
47
 
30
- command :"init" do |c|
31
- c.syntax = 'leeloo init'
32
- c.description = "Initialize leeloo and private keystore"
33
- c.action do |args, options|
34
- abort("a secret key PGP is mandatory") if Keystore::secret_key_empty?
35
- Config::init
36
- say "Initialization completed"
37
- end
38
- end
39
-
40
- command :"list-keystore" do |c|
41
- c.syntax = 'leeloo keystore'
42
- c.description = "Display keystores list"
48
+ command :list do |c|
49
+ c.syntax = 'leeloo list [options]'
50
+ c.description = "Display secrets list of keystore"
43
51
  c.option '--ascii', nil, 'display secrets without unicode tree'
52
+ c.option '--keystore STRING', String, 'a selected keystore'
44
53
 
45
54
  c.action do |args, options|
46
-
47
- Config::list_keystores options.ascii
55
+ keystore = @preferences.keystore(options.keystore)
56
+ OutputFactory.create(options).render_secrets keystore.secrets
48
57
  end
49
58
  end
50
- alias_command :keystore, :"list-keystore"
51
59
 
52
- command :"list-secret" do |c|
53
- c.syntax = 'leeloo list [options]'
54
- c.description = "Display secrets list of keystore"
55
- c.option '--keystore STRING', String, 'a selected keystore'
60
+ command :keystore do |c|
61
+ c.syntax = 'leeloo keystores'
62
+ c.description = "Display current keystores"
56
63
  c.option '--ascii', nil, 'display secrets without unicode tree'
57
64
 
58
65
  c.action do |args, options|
59
- options.default :keystore => Config.default['keystore']
60
-
61
- Secret::list Config.get_keystore(options.keystore), options.ascii
66
+ OutputFactory.create(options).render_preferences @preferences
62
67
  end
63
68
  end
64
- alias_command :list, :"list-secret"
65
- alias_command :secrets, :"list-secret"
66
69
 
67
- command :"add-keystore" do |c|
68
- c.syntax = 'leeloo add-keystore <name> <path>'
69
- c.description = "Add a new keystore"
70
+ command "keystore add" do |c|
71
+ c.syntax = 'leeloo keystore add <name> <path/to/keystore>'
72
+ c.description = "add a keystore"
70
73
 
71
74
  c.action do |args, options|
75
+ abort "name or path is missing" unless args.length == 2
72
76
 
73
- abort "name or path are missing" unless args.length == 2
74
- name = args.first
75
- keystore = args.last
76
-
77
- Keystore.add_keystore name, keystore
78
- Config.add_keystore name, keystore
79
- say "keystore #{name} added"
77
+ @preferences.add_keystore({"name" => args.first, "path" => args.last, "cypher" => "gpg", "vc" => "git"})
78
+ @preferences.keystore(args.first).init
79
+ OutputFactory.create(options).render_preferences @preferences
80
80
  end
81
81
  end
82
82
 
83
- command :"remote-keystore" do |c|
84
- c.syntax = "leeloo remote <repository>"
85
- c.description = "add a remote repository to synchronize keystore"
86
- c.option '--keystore STRING', String, 'a selected keystore'
83
+ command "keystore default" do |c|
84
+ c.syntax = 'leeloo keystore default name'
85
+ c.description = "set the default keystore"
87
86
 
88
87
  c.action do |args, options|
89
- abort "repository is missing" unless args.length == 1
90
- repository = args.first
91
- Keystore.add_remote Config.get_keystore(options.keystore), repository
92
- say "remote added successfully"
93
- end
94
- end
95
- alias_command :remote, :"remote-keystore"
96
-
97
- command :"sync-keystore" do |c|
98
- c.syntax = "leeloo sync"
99
- c.description = "sync secrets with git repository (if configured)"
100
- c.option '--keystore STRING', String, 'a selected keystore'
88
+ abort "name is missing" unless args.length == 1
101
89
 
102
- c.action do |args, options|
103
- options.default :keystore => Config.default['keystore']
104
- synchronized = Keystore.sync_keystore Config.get_keystore(options.keystore)
105
- if synchronized
106
- say "secrets synchronized successfully"
107
- else
108
- abort "call remote-keystore before sync-keystore"
109
- end
90
+ @preferences.set_default_keystore args.first
91
+ OutputFactory.create(options).render_preferences @preferences
110
92
  end
111
93
  end
112
- alias_command :sync, :"sync-keystore"
113
94
 
114
- command :"sign-secret" do |c|
115
- c.syntax = 'leeloo sign'
116
- c.description = "(re)sign all secrets from a given keystore"
95
+ command :read do |c|
96
+ c.syntax = 'leeloo read <name>'
97
+ c.description = "Display a secret from a keystore"
98
+ c.option '--keystore STRING', String, 'a selected keystore'
99
+ c.option '--clipboard', nil, 'copy to clipboard'
117
100
  c.option '--keystore STRING', String, 'a selected keystore'
118
101
 
119
102
  c.action do |args, options|
120
- options.default :keystore => Config.default['keystore']
121
- signed = Secret.sign_secrets Config.get_keystore(options.keystore)
122
- say "secrets signed successfully" if signed
103
+ abort "name is missing" unless args.length == 1
104
+ name = args.first
105
+
106
+ keystore = @preferences.keystore(options.keystore)
107
+ secret = keystore.secret_from_name(name)
108
+ OutputFactory.create(options).render_secret secret
123
109
  end
124
110
  end
125
- alias_command :sign, :"sign-secret"
126
111
 
127
- command :"add-secret" do |c|
128
- c.syntax = 'leeloo add <name>'
129
- c.description = "Add a new secret in a keystore"
112
+ command :write do |c|
113
+ c.syntax = 'leeloo write <name> <secret>'
114
+ c.description = "Write a secret from a keystore"
130
115
  c.option '--keystore STRING', String, 'a selected keystore'
131
116
  c.option '--generate INTEGER', Integer, 'a number of randomized characters'
132
117
  c.option '--stdin', nil, 'secret given by stdin pipe'
@@ -135,87 +120,74 @@ module Leeloo
135
120
  c.action do |args, options|
136
121
  abort "name is missing" unless args.length == 1
137
122
  name = args.first
123
+ phrase = nil
138
124
 
139
- options.default :keystore => Config.default['keystore']
140
- keystore = Config.get_keystore(options.keystore)
141
-
142
- secret = nil
143
- secret = STDIN.read if options.stdin
144
- secret = SecureRandom.base64(32).truncate(options.generate.to_i) if options.generate
125
+ phrase = STDIN.read if options.stdin
126
+ phrase = SecureRandom.base64(32).truncate(options.generate.to_i) if options.generate
145
127
 
146
- unless secret
147
- secret = password "secret"
148
- confirm = password "confirm it"
149
- abort "not the same secret" unless secret == confirm
128
+ unless phrase
129
+ phrase = password "secret"
130
+ confirm = password "confirm it"
131
+ abort "not the same secret" unless phrase == confirm
150
132
  end
151
133
 
152
- Secret.add_secret keystore, name, secret
153
- say "#{name} added successfully"
154
- Clipboard.copy secret if options.clipboard
155
- say secret unless options.clipboard
134
+ keystore = @preferences.keystore(options.keystore)
135
+ secret = keystore.secret_from_name(name)
136
+ secret.write(phrase)
137
+
138
+ OutputFactory.create(options).render_secret secret
156
139
  end
157
140
  end
158
- alias_command :write, :"add-secret"
159
- alias_command :add, :"add-secret"
160
- alias_command :insert, :"add-secret"
161
- alias_command :set, :"add-secret"
162
141
 
163
- command :"read-secret" do |c|
164
- c.syntax = 'leeloo read <name>'
165
- c.description = "Display a secret from a keystore"
142
+ command :translate do |c|
143
+ c.syntax = 'leeloo translate'
144
+ c.description = "translate stdin by replacing key ${my/secret} by the current value"
166
145
  c.option '--keystore STRING', String, 'a selected keystore'
167
- c.option '--clipboard', nil, 'copy to clipboard'
168
- c.option '--to /path/to/file', String, 'for binary file'
169
146
 
170
147
  c.action do |args, options|
171
- abort "name is missing" unless args.length == 1
172
- name = args.first
173
-
174
- options.default :keystore => Config.default['keystore']
175
- keystore = Config.get_keystore(options.keystore)
148
+ keystore = @preferences.keystore(options.keystore)
149
+ text = STDIN.read
150
+ OutputFactory.create(options).render_translate keystore, text
151
+ end
152
+ end
176
153
 
177
- begin
178
- secret = Secret.read_secret keystore, name
154
+ command :remove do |c|
155
+ c.syntax = 'leeloo delete <name>'
156
+ c.description = "Delete a secret from a keystore"
157
+ c.option '--keystore STRING', String, 'a selected keystore'
179
158
 
180
- if (options.to)
181
- File.open(options.to, 'w') { |file| file.write(secret) }
182
- say "stored to #{options.to}"
183
- else
184
- say secret unless options.clipboard
185
- Clipboard.copy secret if options.clipboard
186
- end
159
+ c.action do |args, options|
160
+ abort "name is missing" unless args.length == 1
161
+ name = args.first
187
162
 
188
- rescue
189
- abort "unable to find #{name}"
190
- end
163
+ keystore = @preferences.keystore(options.keystore)
164
+ secret = keystore.secret_from_name(name)
165
+ secret.erase
191
166
  end
192
- alias_command :read, :"read-secret"
193
- alias_command :get, :"read-secret"
194
167
  end
195
168
 
196
- command :"remove-secret" do |c|
197
- c.syntax = 'leeloo remove <name>'
198
- c.description = "Remove a secret from a keystore"
169
+ command :sync do |c|
170
+ c.syntax = 'leeloo sync'
171
+ c.description = "Synchronize a keystore"
199
172
  c.option '--keystore STRING', String, 'a selected keystore'
200
173
 
201
174
  c.action do |args, options|
202
- abort "name is missing" unless args.length == 1
203
- name = args.first
175
+ keystore = @preferences.keystore(options.keystore)
176
+ keystore.sync
177
+ end
178
+ end
204
179
 
205
- options.default :keystore => Config.default['keystore']
206
- keystore = Config.get_keystore(options.keystore)
180
+ command :init do |c|
181
+ c.syntax = 'leeloo init'
182
+ c.description = "Initialize a keystore"
183
+ c.option '--keystore STRING', String, 'a selected keystore'
207
184
 
208
- begin
209
- Secret.delete_secret keystore, name
210
- say "#{name} removed successfully"
211
- rescue
212
- abort "unable to find #{name}"
213
- end
185
+ c.action do |args, options|
186
+ keystore = @preferences.keystore(options.keystore)
187
+ keystore.init
214
188
  end
215
- alias_command :remove, :"remove-secret"
216
- alias_command :delete, :"remove-secret"
217
- alias_command :erase, :"remove-secret"
218
189
  end
190
+
219
191
  end
220
192
  end
221
193
  end
@@ -1,40 +1,163 @@
1
- require 'fileutils'
2
1
  require 'gpgme'
2
+ require 'fileutils'
3
3
  require 'git'
4
4
 
5
5
  module Leeloo
6
+
7
+ class KeystoreFactory
8
+ def self.create keystore
9
+ keystore_created = nil
10
+ case keystore["cypher"]
11
+ when "gpg"
12
+ keystore_created = GpgPrivateLocalFileSystemKeystore.new keystore["name"], keystore["path"]
13
+ else
14
+ keystore_created = PrivateLocalFileSystemKeystore.new keystore["name"], keystore["path"]
15
+ end
16
+
17
+ case keystore["vc"]
18
+ when "git"
19
+ GitKeystoreDecorator.new keystore_created
20
+ else
21
+ keystore_created
22
+ end
23
+ end
24
+ end
25
+
6
26
  class Keystore
7
27
 
8
- def self.secret_key_empty?
9
- GPGME::Key.find(:secret, nil, ).empty?
28
+ attr_reader :name
29
+
30
+ def initialize name
31
+ @name = name
10
32
  end
11
33
 
12
- def self.add_keystore name, path
13
- FileUtils.mkdir_p path
14
- FileUtils.mkdir_p "#{path}/secrets/"
15
- FileUtils.mkdir_p "#{path}/keys/"
34
+ def secrets
35
+ # returns the secrets list
36
+ end
16
37
 
17
- GPGME::Key.find(:public, nil, ).each do |key|
18
- key.export(:output => File.open("#{path}/keys/#{key.uids.first.email}", "w+"))
19
- end
38
+ def secret_of path
39
+ # returns a secret object
40
+ end
20
41
 
21
- g = Git.init path
22
- g.add
23
- g.commit "keystore #{path} added"
42
+ def secret_from_name name
43
+ # returns a secret object
24
44
  end
25
45
 
26
- def self.add_remote path, remote
27
- g = Git.open path
28
- g.add_remote 'origin', remote
46
+ def sync
47
+ # synchronizes the keystore
29
48
  end
30
49
 
31
- def self.sync_keystore path
32
- g = Git.open path
33
- unless g.remotes.empty?
34
- g.pull
35
- g.push
50
+ def init
51
+ # initialize the keystore
52
+ end
53
+
54
+ def == keystore
55
+ self.name == keystore.name
56
+ end
57
+ end
58
+
59
+ class PrivateLocalFileSystemKeystore < Keystore
60
+
61
+ attr_reader :path
62
+
63
+ def initialize name, path
64
+ super name
65
+ @path = path
66
+ FileUtils.mkdir_p "#{@path}/secrets"
67
+ end
68
+
69
+ def secrets
70
+ find_secrets "#{@path}/secrets"
71
+ end
72
+
73
+ def find_secrets path
74
+ elements = []
75
+ Dir.glob("#{path}/**") do |element|
76
+ elements << secret_of(element) unless Dir.exist? element
77
+ elements << find_secrets(element) if Dir.exist? element
36
78
  end
37
- return !g.remotes.empty?
79
+ return elements.flatten
80
+ end
81
+
82
+ def == keystore
83
+ self.name == keystore.name && self.path == keystore.path
84
+ end
85
+
86
+ def secret_of path
87
+ name = path.gsub("#{@path}/secrets/", "")
88
+ LocalFileSystemSecret.new path, name
89
+ end
90
+
91
+ def secret_from_name name
92
+ secret_of "#{path}/secrets/#{name}"
93
+ end
94
+
95
+ end
96
+
97
+ class GpgPrivateLocalFileSystemKeystore < PrivateLocalFileSystemKeystore
98
+
99
+ def initialize name, path
100
+ super name, path
101
+ FileUtils.mkdir_p "#{@path}/keys"
102
+
103
+ @recipients = []
104
+ Dir.glob("#{path}/keys/*") { |key| @recipients << File.basename(key) }
105
+ @recipients.each { |key| GPGME::Key.import(File.open("#{path}/keys/#{key}")) }
106
+ end
107
+
108
+ def init
109
+ super
110
+ GPGME::Key.find(:public, nil, ).each { |key| key.export(:output => File.open("#{path}/keys/#{key.uids.first.email}", "w+")) }
111
+ end
112
+
113
+ def secret_of path
114
+ name = path.gsub("#{@path}/secrets/", "").gsub(".gpg", "")
115
+ GpgLocalFileSystemSecret.new path, name, @recipients
116
+ end
117
+
118
+ def secret_from_name name
119
+ secret_of "#{path}/secrets/#{name}.gpg"
120
+ end
121
+
122
+ end
123
+
124
+ class GitKeystoreDecorator < Keystore
125
+ def initialize keystore
126
+ @keystore = keystore
127
+ Git.init @keystore.path
128
+ @git = Git.open keystore.path
129
+ end
130
+
131
+ def secret_of element
132
+ GitSecretDecorator.new(@git, element)
133
+ end
134
+
135
+ def secret_from_name element
136
+ secret_of @keystore.secret_from_name(element)
137
+ end
138
+
139
+ def secrets
140
+ @keystore.secrets
141
+ end
142
+
143
+ def name
144
+ @keystore.name
145
+ end
146
+
147
+ def path
148
+ @keystore.path
149
+ end
150
+
151
+ def sync
152
+ @git.pull
153
+ @keystore.sync
154
+ @git.push
155
+ end
156
+
157
+ def init
158
+ @keystore.init
159
+ @git.add
160
+ @git.commit "keystore #{@keystore.name} added"
38
161
  end
39
162
 
40
163
  end
@@ -0,0 +1,134 @@
1
+ require 'clipboard'
2
+ require 'tty-table'
3
+ require 'tty-tree'
4
+
5
+ module Leeloo
6
+
7
+ class Output
8
+
9
+ def render_preferences preferences
10
+ end
11
+
12
+ def render_secrets secrets
13
+ end
14
+
15
+ def render_secret secret
16
+ end
17
+
18
+ def render_translate keystore, text
19
+ end
20
+ end
21
+
22
+ class Ascii < Output
23
+
24
+ def render_preferences preferences
25
+ preferences.keystores.each { |keystore| puts keystore.name }
26
+ end
27
+
28
+ def render_secrets secrets
29
+ secrets.sort_by(&:name).each {|secret| puts secret.name}
30
+ end
31
+
32
+ def render_secret secret
33
+ begin
34
+ puts secret.read
35
+ rescue => exception
36
+ puts "#{secret.name} doesn't exist"
37
+ end
38
+ end
39
+
40
+ def render_translate keystore, text
41
+ text.scan(/\$\{.*\}/).each do |secret|
42
+ begin
43
+ text.gsub! secret, (keystore.secret_from_name(secret[2..-2])).read.to_s.strip
44
+ rescue => exception
45
+ # silent
46
+ end
47
+ end
48
+ puts text
49
+ end
50
+ end
51
+
52
+ class Terminal < Ascii
53
+
54
+ def render_preferences preferences
55
+ rows = []
56
+ default_keystore = preferences.default
57
+ preferences.keystores.each do |keystore|
58
+ is_default = '*' if keystore.name == default_keystore
59
+ rows << [keystore.name, keystore.path, is_default ]
60
+ end
61
+ puts TTY::Table.new(header: ['Name', 'Path', 'Default'], rows: rows).render(:ascii)
62
+ end
63
+
64
+ def render_secrets secrets
65
+ hash = {:secrets => []}
66
+ secrets.sort_by(&:name).each { |secret| sort(hash[:secrets], secret.name) }
67
+ puts TTY::Tree.new(hash).render
68
+ end
69
+
70
+ def sort array, element
71
+ if element
72
+ e = element.split("/", 2)
73
+ if e.length > 1
74
+ found = false
75
+ array.each do |a|
76
+ if a.is_a? Hash
77
+ if a[e.first]
78
+ found = true
79
+ sort(a[e.first], e.last)
80
+ break
81
+ end
82
+ end
83
+ end
84
+
85
+ unless found
86
+ array << { e.first => sort([], e.last) }
87
+ end
88
+ else
89
+ array << e.last
90
+ end
91
+ end
92
+ array
93
+ end
94
+ end
95
+
96
+ class ClipboardOutputDecorator < Output
97
+
98
+ def initialize output
99
+ @output = output
100
+ end
101
+
102
+ def render_preferences preferences
103
+ @output.render_preferences preferences
104
+ end
105
+
106
+ def render_secrets secrets
107
+ @output.render_secrets secrets
108
+ end
109
+
110
+ def render_translate keystore, text
111
+ @output.render_translate keystore, text
112
+ end
113
+
114
+ def render_secret secret
115
+
116
+ Signal.trap("INT") do
117
+ Clipboard.clear
118
+ abort "ciao"
119
+ end
120
+
121
+ Clipboard.copy secret.read
122
+ wait = Thread.new do
123
+ puts "cleaning in 30s"
124
+ 30.times {
125
+ print "."
126
+ sleep 1
127
+ }
128
+ end
129
+ wait.join
130
+ Clipboard.clear
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,87 @@
1
+ require 'yaml'
2
+
3
+ module Leeloo
4
+ class Preferences
5
+
6
+ attr_reader :default
7
+
8
+ def initialize
9
+ @keystores = []
10
+ @default = nil
11
+ end
12
+
13
+ def load
14
+ # this method loads all preferences
15
+ self
16
+ end
17
+
18
+ def set_default_keystore name
19
+ @default = name
20
+ end
21
+
22
+ def keystore name=nil
23
+ keystores.find { |k| k.name == (name||@default) }
24
+ end
25
+
26
+ def keystores
27
+ @keystores.map { |k| KeystoreFactory::create k }
28
+ end
29
+
30
+ def add_keystore keystore
31
+ unless @keystores.include? keystore
32
+ @keystores << keystore
33
+ end
34
+ end
35
+ end
36
+
37
+ class PrivateLocalFileSystemPreferences < Preferences
38
+
39
+ DEFAULT_PATH = "#{Dir.home}/.leeloo"
40
+
41
+ def load(path=DEFAULT_PATH)
42
+ @path = path
43
+
44
+ if File.exist? "#{path}/keystores"
45
+ @keystores = YAML.load_file "#{path}/keystores"
46
+ end
47
+
48
+ if File.exist? "#{path}/config"
49
+ config = YAML.load_file "#{path}/config"
50
+ set_default_keystore config["keystore"]
51
+ else
52
+ default_keystore = {
53
+ 'name' => "private",
54
+ 'path' => "#{path}/private",
55
+ 'cypher' => "gpg",
56
+ 'vc' => "git"
57
+ }
58
+ add_keystore default_keystore
59
+ set_default_keystore "private"
60
+ keystore_of("private").init
61
+ end
62
+
63
+ self
64
+ end
65
+
66
+ def keystore_of keystore_name
67
+ keystore = @keystores.find { |keystore| keystore["name"] == keystore_name }
68
+ KeystoreFactory::create keystore
69
+ end
70
+
71
+ def set_default_keystore name
72
+ super name
73
+ config = {
74
+ "keystore" => name
75
+ }
76
+ File.write("#{@path}/config", config.to_yaml)
77
+ end
78
+
79
+ def add_keystore keystore
80
+ super keystore
81
+ FileUtils.mkdir_p keystore["path"]
82
+ File.write("#{@path}/keystores", @keystores.to_yaml)
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -1,84 +1,105 @@
1
1
  require 'gpgme'
2
- require 'tty-tree'
3
- require 'git'
4
2
  require 'fileutils'
5
3
 
6
4
  module Leeloo
7
-
8
5
  class Secret
9
6
 
10
- def self.list(keystore, ascii)
11
- if ascii
12
- Dir.glob("#{keystore}/secrets/**/*.gpg")
13
- .sort
14
- .reject { |path| File.directory? path }
15
- .each { |secret| puts secret.gsub(/#{keystore}\/secrets\//, '').gsub(/\.gpg/, '') }
16
- else
17
- puts TTY::Tree.new("#{keystore}/secrets").render.gsub(/\.gpg/, '')
18
- end
7
+ attr_reader :name
8
+
9
+ def initialize name
10
+ @name = name
19
11
  end
20
12
 
21
- def self.add_secret(keystore, name, secret)
22
- recipients = []
23
- Dir.foreach("#{keystore}/keys") { |key| recipients << File.basename(key, ".*") unless File.directory? key }
13
+ def == secret
14
+ @name == secret.name
15
+ end
24
16
 
25
- FileUtils.mkdir_p File.dirname "#{keystore}/secrets/#{name}"
17
+ def read
18
+ # returns the secret
19
+ end
26
20
 
27
- crypto = GPGME::Crypto.new :always_trust => true
28
- crypto.encrypt secret,
29
- :output => File.open("#{keystore}/secrets/#{name}.gpg","w+"),
30
- :recipients => recipients
21
+ def write phrase
22
+ # write the secret
23
+ end
31
24
 
32
- g = Git.open keystore
33
- g.add "#{keystore}/secrets/#{name}.gpg"
34
- g.commit "secret #{name} added"
25
+ def erase
26
+ # erase the secret
35
27
  end
36
28
 
37
- def self.read_secret(keystore, name)
38
- crypto = GPGME::Crypto.new
39
- crypto.decrypt File.open("#{keystore}/secrets/#{name}.gpg")
29
+ end
30
+
31
+ class LocalFileSystemSecret < Secret
32
+
33
+ attr_reader :pathname
34
+
35
+ def initialize pathname, name
36
+ super name
37
+ @pathname = pathname
40
38
  end
41
39
 
42
- def self.delete_secret(keystore, name)
43
- g = Git.open keystore
44
- g.remove "#{keystore}/secrets/#{name}.gpg"
45
- g.commit "secret #{name} removed"
40
+ def read
41
+ File.read @pathname
46
42
  end
47
43
 
48
- def self.sign_secrets keystore
44
+ def write phrase
45
+ FileUtils.mkdir_p File.dirname @pathname
46
+ File.write @pathname, phrase
47
+ end
49
48
 
50
- g = Git.open keystore
49
+ def erase
50
+ File.delete @pathname
51
+ end
51
52
 
52
- recipients = []
53
- Dir.foreach("#{keystore}/keys") do |key|
54
- unless File.directory? key
55
- recipients << File.basename(key, ".*")
56
- GPGME::Key.import(File.open("#{keystore}/keys/#{key}"))
57
- end
58
- end
53
+ end
59
54
 
60
- crypto = GPGME::Crypto.new :always_trust => true
61
- find_secrets("#{keystore}/secrets").each do |secret|
62
- say "."
63
- decrypted = crypto.decrypt File.open(secret)
64
- crypto.encrypt decrypted,
65
- :output => File.open(secret,"w+"),
66
- :recipients => recipients
67
- g.add secret
68
- end
55
+ class GpgLocalFileSystemSecret < LocalFileSystemSecret
69
56
 
70
- g.commit "sync"
71
- return true
57
+ def initialize pathname, name, recipients
58
+ super pathname, name
59
+ @recipients = recipients
60
+ @crypto = GPGME::Crypto.new :always_trust => true
61
+ end
62
+
63
+ def read
64
+ @crypto.decrypt File.open(@pathname)
72
65
  end
73
66
 
74
- def self.find_secrets path
75
- elements = []
76
- Dir.glob("#{path}/**") do |element|
77
- elements << element unless Dir.exist? element
78
- elements << find_secrets(element) if Dir.exist? element
79
- end
80
- return elements.flatten
67
+ def write phrase
68
+ FileUtils.mkdir_p File.dirname @pathname
69
+ @crypto.encrypt phrase,
70
+ :output => File.open(@pathname,"w+"),
71
+ :recipients => @recipients
81
72
  end
82
73
 
83
74
  end
75
+
76
+ class GitSecretDecorator < Secret
77
+
78
+ def initialize git, secret
79
+ @git = git
80
+ @secret = secret
81
+ end
82
+
83
+ def name
84
+ @secret.name
85
+ end
86
+
87
+ def read
88
+ @secret.read
89
+ end
90
+
91
+ def write phrase
92
+ @secret.write phrase
93
+ @git.add @secret.pathname
94
+ @git.commit "secret #{@secret.name} added"
95
+ end
96
+
97
+ def erase
98
+ @secret.erase
99
+ @git.remove @secret.pathname
100
+ @git.commit "secret #{@secret.name} removed"
101
+ end
102
+
103
+ end
104
+
84
105
  end
@@ -1,4 +1,4 @@
1
1
  module Leeloo
2
- VERSION = '0.0.16'.freeze
2
+ VERSION = '0.1.0'.freeze
3
3
  DESCRIPTION = "The easiest way to share securely your secrets".freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: leeloo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.16
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvek
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-27 00:00:00.000000000 Z
11
+ date: 2019-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -53,89 +53,75 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.5'
55
55
  - !ruby/object:Gem::Dependency
56
- name: terminal-table
56
+ name: tty-table
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '1.8'
61
+ version: '0.10'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '1.8'
68
+ version: '0.10'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: tty-tree
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '0.1'
75
+ version: '0.3'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '0.1'
82
+ version: '0.3'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: clipboard
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '1.1'
89
+ version: '1.3'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '1.1'
97
- - !ruby/object:Gem::Dependency
98
- name: ffi
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '1.9'
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '1.9'
96
+ version: '1.3'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: bundler
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
101
  - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: '1.15'
103
+ version: '2.0'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
108
  - - "~>"
123
109
  - !ruby/object:Gem::Version
124
- version: '1.15'
110
+ version: '2.0'
125
111
  - !ruby/object:Gem::Dependency
126
112
  name: rake
127
113
  requirement: !ruby/object:Gem::Requirement
128
114
  requirements:
129
115
  - - "~>"
130
116
  - !ruby/object:Gem::Version
131
- version: '10.0'
117
+ version: '12.0'
132
118
  type: :development
133
119
  prerelease: false
134
120
  version_requirements: !ruby/object:Gem::Requirement
135
121
  requirements:
136
122
  - - "~>"
137
123
  - !ruby/object:Gem::Version
138
- version: '10.0'
124
+ version: '12.0'
139
125
  - !ruby/object:Gem::Dependency
140
126
  name: rspec
141
127
  requirement: !ruby/object:Gem::Requirement
@@ -161,8 +147,9 @@ files:
161
147
  - exe/leeloo
162
148
  - lib/leeloo.rb
163
149
  - lib/leeloo/command.rb
164
- - lib/leeloo/config.rb
165
150
  - lib/leeloo/keystore.rb
151
+ - lib/leeloo/output.rb
152
+ - lib/leeloo/preferences.rb
166
153
  - lib/leeloo/secret.rb
167
154
  - lib/leeloo/version.rb
168
155
  homepage: https://github.com/sylvek
@@ -184,8 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
171
  - !ruby/object:Gem::Version
185
172
  version: '0'
186
173
  requirements: []
187
- rubyforge_project:
188
- rubygems_version: 2.7.7
174
+ rubygems_version: 3.0.4
189
175
  signing_key:
190
176
  specification_version: 4
191
177
  summary: The easiest way to share securely your secrets
@@ -1,62 +0,0 @@
1
- require 'yaml'
2
- require 'terminal-table'
3
-
4
- module Leeloo
5
- class Config
6
-
7
- PATH = "#{Dir.home}/.leeloo"
8
-
9
- @@keystores = []
10
-
11
- @@default = { "keystore" => "private" }
12
-
13
- def self.default
14
- @@default
15
- end
16
-
17
- def self.init
18
- Keystore::add_keystore "private", "#{PATH}/private"
19
- Config::add_keystore "private", "#{PATH}/private"
20
- end
21
-
22
- def self.list_keystores(ascii)
23
- if ascii
24
- @@keystores.each { |keystore| puts keystore['name'] }
25
- else
26
- rows = []
27
- @@keystores.each do |keystore|
28
- is_default = '*' if keystore['name'] == @@default['keystore']
29
- rows << [keystore['name'], keystore['path'], is_default ]
30
- end
31
- say Terminal::Table.new :headings => ['Name', 'Path', 'Default'], :rows => rows
32
- end
33
- end
34
-
35
- def self.load
36
- FileUtils.mkdir_p PATH
37
- if File.exist? "#{PATH}/keystores"
38
- @@keystores = YAML.load_file "#{PATH}/keystores"
39
- end
40
- if File.exist? "#{PATH}/config"
41
- @@default = YAML.load_file "#{PATH}/config"
42
- end
43
- end
44
-
45
- def self.add_keystore name, path
46
- keystore = { 'name' => name, 'path' => path}
47
- unless @@keystores.include? keystore
48
- @@keystores << keystore
49
- File.write("#{PATH}/keystores", @@keystores.to_yaml)
50
- end
51
- end
52
-
53
- def self.get_keystore name
54
- @@keystores.each do |keystore|
55
- return keystore['path'] if keystore['name'] == name
56
- end
57
-
58
- raise "keystore not found"
59
- end
60
-
61
- end
62
- end