leeloo 0.3.0 → 0.5.1

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: f395de66b0d5e6550921e66339d5dcd2df759bc38662d09b4bc650a510cb4dd8
4
- data.tar.gz: 9b311918a29972cd5bca9dc0c856bf37e8bacf59db8b6b954ff1ebd5d4790eeb
3
+ metadata.gz: 236ca14fba0124365c37165ecd103e9342bc8c981ef4ed23bbf4afb73752e7b4
4
+ data.tar.gz: 53d104d94f393fb44e2532f7e59b91164461b14710d504b71730d5a8e549c8d9
5
5
  SHA512:
6
- metadata.gz: 36703d60d92d086540d33d16171d1aa290595f4d408db707ac22fdeb11077f8d393ffd861c25aaafa331441586b27b98a8b4f16ac33f61da654c6e27b992ff3f
7
- data.tar.gz: dde8201181fbfb863f091accb97bd2fea285e1cdb7bc75c7ec94ee2c9ce4c1783b8b0c55d593b55b5446506c3826862f32d989657a64873205d4052dbba2ea1b
6
+ metadata.gz: 82d0d1f608c8adece27b0ff8e40c6eb01a953b1d46cdc3e28f7bfa86031a73b4989f11c9519491b6a9c1db7b3a05bd8d15f32ddb3b42aad805652fd459bb115a
7
+ data.tar.gz: 12b643a433090504e471723ed2aa519c4aee77f5fb70151a9766f78a7f62b52b6db909b6c377a3cb4762fb2959b93ea5715fbe42887e782353cc68f4a0674aca
@@ -9,29 +9,9 @@ end
9
9
 
10
10
  module Leeloo
11
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
-
28
12
  class Command
29
13
  include Commander::Methods
30
14
 
31
- def initialize
32
- @preferences = PrivateLocalFileSystemPreferences.new.load
33
- end
34
-
35
15
  def run
36
16
  program :name, 'leeloo'
37
17
  program :version, Leeloo::VERSION
@@ -43,17 +23,34 @@ module Leeloo
43
23
  program :help, 'GitHub', 'https://github.com/sylvek'
44
24
  program :help_formatter, :compact
45
25
 
46
- default_command :"list"
26
+ default_command :wrapper
27
+
28
+ command :wrapper do |c|
29
+ c.syntax = 'leeloo [options]'
30
+ c.description = "Display secrets"
31
+ c.option '--ascii', nil, 'display secrets without unicode tree'
32
+ c.option '--keystore STRING', String, 'a selected keystore'
33
+ c.option '--clipboard', nil, 'copy to clipboard'
34
+ c.action do |args, options|
35
+ unless args == []
36
+ name = args.first
37
+ ctl = SecretController.new(options)
38
+ ctl.read(name)
39
+ ctl.display
40
+ else
41
+ SecretsController.new(options).display
42
+ end
43
+ end
44
+ end
47
45
 
48
46
  command :list do |c|
49
47
  c.syntax = 'leeloo list [options]'
50
- c.description = "Display secrets list of keystore"
48
+ c.description = "Display secrets list of stored on a keystore"
51
49
  c.option '--ascii', nil, 'display secrets without unicode tree'
52
50
  c.option '--keystore STRING', String, 'a selected keystore'
53
51
 
54
52
  c.action do |args, options|
55
- keystore = @preferences.keystore(options.keystore)
56
- OutputFactory.create(options).render_secrets keystore.secrets
53
+ SecretsController.new(options).display
57
54
  end
58
55
  end
59
56
 
@@ -66,10 +63,9 @@ module Leeloo
66
63
  c.action do |args, options|
67
64
  abort "name is missing" unless args.length == 1
68
65
  name = args.first
69
-
70
- keystore = @preferences.keystore(options.keystore)
71
- secrets = keystore.secrets.select { |secret| secret.name.downcase.include? name.downcase } || []
72
- OutputFactory.create(options).render_secrets secrets
66
+ ctl = SecretsController.new(options)
67
+ ctl.search(name)
68
+ ctl.display
73
69
  end
74
70
  end
75
71
 
@@ -79,7 +75,7 @@ module Leeloo
79
75
  c.option '--ascii', nil, 'display secrets without unicode tree'
80
76
 
81
77
  c.action do |args, options|
82
- OutputFactory.create(options).render_preferences @preferences
78
+ KeystoreController.new(options).display
83
79
  end
84
80
  end
85
81
 
@@ -87,10 +83,12 @@ module Leeloo
87
83
  c.syntax = 'leeloo keystore remove <name>'
88
84
  c.description = "remove a keystore (path/to/keystore is not destroyed)"
89
85
 
90
- c.action do |args, options|
86
+ c.action do |args, options|args
91
87
  abort "name is missing" unless args.length == 1
92
- @preferences.remove_keystore args.first
93
- OutputFactory.create(options).render_preferences @preferences
88
+ name = args.first
89
+ ctl = KeystoreController.new(options)
90
+ ctl.remove(name)
91
+ ctl.display
94
92
  end
95
93
  end
96
94
 
@@ -100,10 +98,11 @@ module Leeloo
100
98
 
101
99
  c.action do |args, options|
102
100
  abort "name or path is missing" unless args.length == 2
103
-
104
- @preferences.add_keystore({"name" => args.first, "path" => args.last, "cypher" => "gpg", "vc" => "git"})
105
- @preferences.keystore(args.first).init
106
- OutputFactory.create(options).render_preferences @preferences
101
+ name = args.first
102
+ path = args.last
103
+ ctl = KeystoreController.new(options)
104
+ ctl.add(name, path)
105
+ ctl.display
107
106
  end
108
107
  end
109
108
 
@@ -113,9 +112,62 @@ module Leeloo
113
112
 
114
113
  c.action do |args, options|
115
114
  abort "name is missing" unless args.length == 1
115
+ name = args.first
116
+ ctl = KeystoreController.new(options)
117
+ ctl.set_default(name)
118
+ ctl.display
119
+ end
120
+ end
121
+
122
+ command :key do |c|
123
+ c.syntax = 'leeloo keys'
124
+ c.description = "list keys from this keystore"
125
+ c.option '--ascii', nil, 'display secrets without unicode tree'
126
+ c.option '--keystore STRING', String, 'a selected keystore'
127
+
128
+ c.action do |args, options|
129
+ ctl = KeysController.new(options)
130
+ ctl.display
131
+ end
132
+ end
133
+
134
+ command "key sync" do |c|
135
+ c.syntax = 'leeloo keys sync'
136
+ c.description = "synchronize secrets with keys"
137
+ c.option '--keystore STRING', String, 'a selected keystore'
138
+
139
+ c.action do |args, options|
140
+ ctl = KeysController.new(options)
141
+ ctl.sync
142
+ ctl.display
143
+ end
144
+ end
116
145
 
117
- @preferences.set_default_keystore args.first
118
- OutputFactory.create(options).render_preferences @preferences
146
+ command "key add" do |c|
147
+ c.syntax = 'leeloo key add <email>'
148
+ c.description = "add a dedicated key"
149
+ c.option '--keystore STRING', String, 'a selected keystore'
150
+
151
+ c.action do |args, options|
152
+ abort "email is missing" unless args.length == 1
153
+ email = args.first
154
+ ctl = KeysController.new(options)
155
+ ctl.add_key(email)
156
+ ctl.display
157
+ end
158
+ end
159
+
160
+ command "key remove" do |c|
161
+ c.syntax = 'leeloo key remove <email>'
162
+ c.description = "remove a dedicated key"
163
+ c.option '--keystore STRING', String, 'a selected keystore'
164
+
165
+ c.action do |args, options|
166
+ abort "email is missing" unless args.length == 1
167
+ email = args.first
168
+ ctl = KeysController.new(options)
169
+ ctl.remove_key(email)
170
+ ctl.display
119
171
  end
120
172
  end
121
173
 
@@ -124,15 +176,13 @@ module Leeloo
124
176
  c.description = "Display a secret from a keystore"
125
177
  c.option '--keystore STRING', String, 'a selected keystore'
126
178
  c.option '--clipboard', nil, 'copy to clipboard'
127
- c.option '--keystore STRING', String, 'a selected keystore'
128
179
 
129
180
  c.action do |args, options|
130
181
  abort "name is missing" unless args.length == 1
131
182
  name = args.first
132
-
133
- keystore = @preferences.keystore(options.keystore)
134
- secret = keystore.secret_from_name(name)
135
- OutputFactory.create(options).render_secret secret
183
+ ctl = SecretController.new(options)
184
+ ctl.read(name)
185
+ ctl.display
136
186
  end
137
187
  end
138
188
 
@@ -147,22 +197,9 @@ module Leeloo
147
197
  c.action do |args, options|
148
198
  abort "name is missing" unless args.length == 1
149
199
  name = args.first
150
- phrase = nil
151
-
152
- phrase = STDIN.read if options.stdin
153
- phrase = SecureRandom.base64(32).truncate(options.generate.to_i) if options.generate
154
-
155
- unless phrase
156
- phrase = password "secret"
157
- confirm = password "confirm it"
158
- abort "not the same secret" unless phrase == confirm
159
- end
160
-
161
- keystore = @preferences.keystore(options.keystore)
162
- secret = keystore.secret_from_name(name)
163
- secret.write(phrase)
164
-
165
- OutputFactory.create(options).render_secret secret
200
+ ctl = SecretController.new(options)
201
+ ctl.write(name)
202
+ ctl.display
166
203
  end
167
204
  end
168
205
 
@@ -172,9 +209,9 @@ module Leeloo
172
209
  c.option '--keystore STRING', String, 'a selected keystore'
173
210
 
174
211
  c.action do |args, options|
175
- keystore = @preferences.keystore(options.keystore)
176
- text = STDIN.read
177
- OutputFactory.create(options).render_translate keystore, text
212
+ ctl = TranslateController.new(options)
213
+ ctl.translate
214
+ ctl.display
178
215
  end
179
216
  end
180
217
 
@@ -186,32 +223,84 @@ module Leeloo
186
223
  c.action do |args, options|
187
224
  abort "name is missing" unless args.length == 1
188
225
  name = args.first
189
-
190
- keystore = @preferences.keystore(options.keystore)
191
- secret = keystore.secret_from_name(name)
192
- secret.erase
226
+ ctl = SecretController.new(options)
227
+ ctl.remove(name)
228
+ #ctl.display
229
+ puts "done."
193
230
  end
194
231
  end
195
232
 
196
- command :sync do |c|
233
+ command "keystore sync" do |c|
197
234
  c.syntax = 'leeloo sync'
198
235
  c.description = "Synchronize a keystore"
199
236
  c.option '--keystore STRING', String, 'a selected keystore'
200
237
 
201
238
  c.action do |args, options|
202
- keystore = @preferences.keystore(options.keystore)
203
- keystore.sync
239
+ ctl = KeystoreController.new(options)
240
+ ctl.sync
241
+ ctl.display
242
+ end
243
+ end
244
+
245
+ command "keystore export" do |c|
246
+ c.syntax = 'leeloo export'
247
+ c.description = "Export all secrets from a keystore"
248
+ c.option '--keystore STRING', String, 'a selected keystore'
249
+
250
+ c.action do |args, options|
251
+ ctl = ExportController.new(options)
252
+ ctl.display
204
253
  end
205
254
  end
206
255
 
207
- command :init do |c|
256
+ command "keystore init" do |c|
208
257
  c.syntax = 'leeloo init'
209
258
  c.description = "Initialize a keystore"
210
259
  c.option '--keystore STRING', String, 'a selected keystore'
211
260
 
212
261
  c.action do |args, options|
213
- keystore = @preferences.keystore(options.keystore)
214
- keystore.init
262
+ ctl = KeystoreController.new(options)
263
+ ctl.init
264
+ ctl.display
265
+ end
266
+ end
267
+
268
+ command :share do |c|
269
+ c.syntax = 'leeloo share <name>'
270
+ c.description = "share a secret with someone"
271
+ c.option '--keystore STRING', String, 'a selected keystore'
272
+
273
+ c.action do |args, options|
274
+ abort "name is missing" unless args.length == 1
275
+ name = args.first
276
+ ctl = ShareController.new(options)
277
+ ctl.token(name)
278
+ ctl.display
279
+ ctl.start_server
280
+ end
281
+ end
282
+
283
+ command :token do |c|
284
+ c.syntax = 'leeloo token <name>'
285
+ c.description = "generate an access token for a given secret"
286
+ c.option '--keystore STRING', String, 'a selected keystore'
287
+
288
+ c.action do |args, options|
289
+ abort "name is missing" unless args.length == 1
290
+ name = args.first
291
+ ctl = ShareController.new(options)
292
+ ctl.token(name)
293
+ ctl.display
294
+ end
295
+ end
296
+
297
+ command :server do |c|
298
+ c.syntax = 'leeloo server'
299
+ c.description = "start a server access token"
300
+
301
+ c.action do |args, options|
302
+ ctl = ShareController.new(options)
303
+ ctl.start_server
215
304
  end
216
305
  end
217
306
 
@@ -0,0 +1,152 @@
1
+ module Leeloo
2
+ class OutputFactory
3
+ def self.create options
4
+ output = nil
5
+ if options.ascii
6
+ output = Ascii.new
7
+ else
8
+ output = Terminal.new
9
+ end
10
+ if options.clipboard
11
+ ClipboardOutputDecorator.new output
12
+ else
13
+ output
14
+ end
15
+ end
16
+ end
17
+
18
+ class Controller
19
+ def display
20
+ end
21
+ end
22
+
23
+ class PrivateLocalFileSystemController < Controller
24
+ def initialize options
25
+ @preferences = PrivateLocalFileSystemPreferences.new.load
26
+ @keystore = @preferences.keystore(options.keystore)
27
+ @output = OutputFactory.create(options)
28
+ @options = options
29
+ end
30
+ end
31
+
32
+ class SecretsController < PrivateLocalFileSystemController
33
+ def initialize options
34
+ super options
35
+ @secrets = @keystore.secrets
36
+ end
37
+ def search name
38
+ @secrets = @secrets.select { |secret| secret.name.downcase.include? name.downcase } || []
39
+ end
40
+ def list
41
+ @secrets
42
+ end
43
+ def display
44
+ @output.render_secrets @secrets
45
+ end
46
+ end
47
+
48
+ class ExportController < PrivateLocalFileSystemController
49
+ def display
50
+ @keystore.secrets.each do |secret|
51
+ @output.render_name_and_secret(secret.name, @keystore.secret_from_name(secret.name))
52
+ end
53
+ end
54
+ end
55
+
56
+ class KeysController < PrivateLocalFileSystemController
57
+ def add_key email
58
+ @keystore.add_key(email)
59
+ end
60
+ def remove_key email
61
+ @keystore.remove_key(email)
62
+ end
63
+ def sync
64
+ @keystore.secrets.each do |secret|
65
+ phrase = @keystore.secret_from_name(secret.name).read
66
+ @keystore.secret_from_name(secret.name).write(phrase)
67
+ end
68
+ end
69
+ def display
70
+ @keys = @keystore.keys
71
+ @output.render_keys @keys
72
+ end
73
+ end
74
+
75
+ class SecretController < PrivateLocalFileSystemController
76
+ def read name
77
+ @secret = @keystore.secret_from_name(name)
78
+ end
79
+ def write name
80
+ phrase = nil
81
+
82
+ phrase = STDIN.read if @options.stdin
83
+ phrase = SecureRandom.base64(32).truncate(@options.generate.to_i) if @options.generate
84
+
85
+ unless phrase
86
+ phrase = password "secret"
87
+ confirm = password "confirm it"
88
+ abort "not the same secret" unless phrase == confirm
89
+ end
90
+
91
+ @secret = @keystore.secret_from_name(name)
92
+ @secret.write(phrase)
93
+ end
94
+ def remove name
95
+ @secret = @keystore.secret_from_name(name)
96
+ @secret.erase
97
+ end
98
+ def display
99
+ @output.render_secret @secret
100
+ end
101
+ end
102
+
103
+ class TranslateController < PrivateLocalFileSystemController
104
+ def translate
105
+ @text = STDIN.read
106
+ @text.scan(/\$\{.*\}/).each do |secret|
107
+ begin
108
+ @text.gsub! secret, (@keystore.secret_from_name(secret[2..-2])).read.to_s.strip
109
+ rescue => exception
110
+ # silent
111
+ end
112
+ end
113
+ end
114
+ def display
115
+ @output.render_text @text
116
+ end
117
+ end
118
+
119
+ class KeystoreController < PrivateLocalFileSystemController
120
+ def add name, path
121
+ @preferences.add_keystore({"name" => name, "path" => path, "cypher" => "gpg", "vc" => "git"})
122
+ @preferences.keystore(name).init
123
+ end
124
+ def remove name
125
+ @preferences.remove_keystore name
126
+ end
127
+ def set_default name
128
+ @preferences.set_default_keystore name
129
+ end
130
+ def sync
131
+ @keystore.sync
132
+ end
133
+ def init
134
+ @keystore.init
135
+ end
136
+ def display
137
+ @output.render_preferences @preferences
138
+ end
139
+ end
140
+
141
+ class ShareController < PrivateLocalFileSystemController
142
+ def token name
143
+ @footprint = @keystore.footprint_of(name)
144
+ end
145
+ def start_server
146
+ Server.new.start @preferences
147
+ end
148
+ def display
149
+ @output.render_footprint @footprint
150
+ end
151
+ end
152
+ end
@@ -1,6 +1,7 @@
1
1
  require 'gpgme'
2
2
  require 'fileutils'
3
3
  require 'git'
4
+ require 'base64'
4
5
 
5
6
  module Leeloo
6
7
 
@@ -51,6 +52,14 @@ module Leeloo
51
52
  # initialize the keystore
52
53
  end
53
54
 
55
+ def footprint name
56
+ # footprint a given secret path
57
+ end
58
+
59
+ def secret_from_footprint footprint
60
+ # returns a secret object
61
+ end
62
+
54
63
  def == keystore
55
64
  self.name == keystore.name
56
65
  end
@@ -70,6 +79,10 @@ module Leeloo
70
79
  find_secrets "#{@path}/secrets"
71
80
  end
72
81
 
82
+ def keys
83
+ []
84
+ end
85
+
73
86
  def find_secrets path
74
87
  elements = []
75
88
  Dir.glob("#{path}/**") do |element|
@@ -92,6 +105,19 @@ module Leeloo
92
105
  secret_of "#{path}/secrets/#{name}"
93
106
  end
94
107
 
108
+ def footprint_of name
109
+ secret = secret_from_name name
110
+ { "footprint" => secret.footprint, "keystore" => self.name, "secret" => secret.name }
111
+ end
112
+
113
+ def secret_from_footprint footprint
114
+ secret = secret_from_name footprint["secret"]
115
+ unless secret.footprint == footprint["footprint"]
116
+ raise "footprint is not valid"
117
+ end
118
+ secret
119
+ end
120
+
95
121
  end
96
122
 
97
123
  class GpgPrivateLocalFileSystemKeystore < PrivateLocalFileSystemKeystore
@@ -99,7 +125,10 @@ module Leeloo
99
125
  def initialize name, path
100
126
  super name, path
101
127
  FileUtils.mkdir_p "#{@path}/keys"
128
+ populate_recipients
129
+ end
102
130
 
131
+ def populate_recipients
103
132
  @recipients = []
104
133
  Dir.glob("#{path}/keys/*") { |key| @recipients << File.basename(key) }
105
134
  @recipients.each { |key| GPGME::Key.import(File.open("#{path}/keys/#{key}")) }
@@ -107,7 +136,30 @@ module Leeloo
107
136
 
108
137
  def init
109
138
  super
110
- GPGME::Key.find(:public, nil, ).each { |key| key.export(:output => File.open("#{path}/keys/#{key.uids.first.email}", "w+")) }
139
+ File.write("#{@path}/keys/do_not_remove_me", "do not remove me")
140
+ end
141
+
142
+ def keys
143
+ available = GPGME::Key.find(:public, nil, ).map { |key| key.email }
144
+ actual = Dir.glob("#{@path}/keys/**").map { |path| path.split('/').last }
145
+ available.map { |email| actual.include?(email) ? "#{email}::true" : "#{email}::false" }
146
+ end
147
+
148
+ def add_key email
149
+ paths = []
150
+ GPGME::Key.find(:public, email).each do |key|
151
+ key.export(:output => File.open("#{path}/keys/#{key.uids.first.email}", "w+"))
152
+ paths << "#{path}/keys/#{key.uids.first.email}"
153
+ end
154
+ return paths
155
+ end
156
+
157
+ def remove_key email
158
+ if File.exist?("#{path}/keys/#{email}")
159
+ File.delete("#{path}/keys/#{email}")
160
+ return "#{path}/keys/#{email}"
161
+ end
162
+ return nil
111
163
  end
112
164
 
113
165
  def secret_of path
@@ -119,6 +171,20 @@ module Leeloo
119
171
  secret_of "#{path}/secrets/#{name}.gpg"
120
172
  end
121
173
 
174
+ def footprint_of name
175
+ footprint = super name
176
+ footprint["sign"] = Base64.strict_encode64 GPGME::Crypto.new.sign(footprint["footprint"]).to_s
177
+ footprint
178
+ end
179
+
180
+ def secret_from_footprint footprint
181
+ data = GPGME::Crypto.new.verify(Base64.strict_decode64 footprint["sign"]) { |signature| signature.valid? }
182
+ if data.read == footprint["footprint"]
183
+ super footprint
184
+ else
185
+ raise "signature is not valid"
186
+ end
187
+ end
122
188
  end
123
189
 
124
190
  class GitKeystoreDecorator < Keystore
@@ -136,6 +202,25 @@ module Leeloo
136
202
  secret_of @keystore.secret_from_name(element)
137
203
  end
138
204
 
205
+ def keys
206
+ @keystore.keys
207
+ end
208
+
209
+ def add_key email
210
+ @keystore.add_key(email).each do |path|
211
+ @git.add path
212
+ @git.commit "#{email} added"
213
+ end
214
+ end
215
+
216
+ def remove_key email
217
+ deleted = @keystore.remove_key email
218
+ if deleted
219
+ @git.add deleted
220
+ @git.commit "#{email} removed"
221
+ end
222
+ end
223
+
139
224
  def secrets
140
225
  @keystore.secrets
141
226
  end
@@ -148,6 +233,14 @@ module Leeloo
148
233
  @keystore.path
149
234
  end
150
235
 
236
+ def footprint_of element
237
+ @keystore.footprint_of element
238
+ end
239
+
240
+ def secret_from_footprint footprint
241
+ @keystore.secret_from_footprint footprint
242
+ end
243
+
151
244
  def sync
152
245
  @git.pull
153
246
  @keystore.sync
data/lib/leeloo/output.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  require 'clipboard'
2
2
  require 'tty-table'
3
3
  require 'tty-tree'
4
+ require 'json'
5
+ require 'base64'
6
+ require 'socket'
4
7
 
5
8
  module Leeloo
6
9
 
@@ -15,7 +18,19 @@ module Leeloo
15
18
  def render_secret secret
16
19
  end
17
20
 
18
- def render_translate keystore, text
21
+ def render_text text
22
+ end
23
+
24
+ def render_name_and_secret name, secret
25
+ end
26
+
27
+ def render_keys keys
28
+ end
29
+
30
+ def render_footprint footprint
31
+ end
32
+
33
+ def render_share footprint
19
34
  end
20
35
  end
21
36
 
@@ -37,16 +52,24 @@ module Leeloo
37
52
  end
38
53
  end
39
54
 
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
55
+ def render_name_and_secret name, secret
56
+ self.render_text name
57
+ self.render_secret secret
58
+ self.render_text '------'
59
+ end
60
+
61
+ def render_text text
48
62
  puts text
49
63
  end
64
+
65
+ def render_keys keys
66
+ self.render_text keys
67
+ end
68
+
69
+ def render_footprint footprint
70
+ puts "token:"
71
+ puts Base64.strict_encode64 footprint.to_json
72
+ end
50
73
  end
51
74
 
52
75
  class Terminal < Ascii
@@ -62,11 +85,21 @@ module Leeloo
62
85
  end
63
86
 
64
87
  def render_secrets secrets
65
- hash = {:secrets => []}
66
- secrets.sort_by(&:name).each { |secret| sort(hash[:secrets], secret.name) }
88
+ hash = {'Password Store' => []}
89
+ secrets.sort_by(&:name).each { |secret| sort(hash['Password Store'], secret.name) }
67
90
  puts TTY::Tree.new(hash).render
68
91
  end
69
92
 
93
+ def render_keys keys
94
+ rows = []
95
+ keys.each do |key|
96
+ splitted = key.split('::')
97
+ is_present = '*' if splitted[1] == 'true'
98
+ rows << [splitted[0], is_present]
99
+ end
100
+ puts TTY::Table.new(header: ['Email', 'Selected'], rows: rows).render(:ascii)
101
+ end
102
+
70
103
  def sort array, element
71
104
  if element
72
105
  e = element.split("/", 2)
@@ -107,16 +140,16 @@ module Leeloo
107
140
  @output.render_secrets secrets
108
141
  end
109
142
 
110
- def render_translate keystore, text
111
- @output.render_translate keystore, text
143
+ def render_text text
144
+ @output.render_text text
112
145
  end
113
146
 
114
147
  def render_secret secret
115
148
 
116
149
  Signal.trap("INT") do
117
150
  Clipboard.clear
118
- abort "ciao"
119
- end
151
+ abort "cleared"
152
+ end
120
153
 
121
154
  Clipboard.copy secret.read
122
155
  wait = Thread.new do
@@ -129,6 +162,14 @@ module Leeloo
129
162
  wait.join
130
163
  Clipboard.clear
131
164
  end
165
+
166
+ def render_footprint footprint
167
+ @output.render_footprint footprint
168
+ end
169
+
170
+ def render_share footprint
171
+ @output.render_share footprint
172
+ end
132
173
  end
133
174
 
134
175
  end
@@ -34,8 +34,9 @@ module Leeloo
34
34
  end
35
35
 
36
36
  def remove_keystore name
37
+ abort "you can not remove default keystore" if name == @default
37
38
  keystore = @keystores.find { |k| k["name"] == name }
38
- if keystore !=nil
39
+ if keystore != nil
39
40
  @keystores.delete keystore
40
41
  end
41
42
  end
@@ -70,8 +71,8 @@ module Leeloo
70
71
  self
71
72
  end
72
73
 
73
- def keystore_of keystore_name
74
- keystore = @keystores.find { |keystore| keystore["name"] == keystore_name }
74
+ def keystore_of name
75
+ keystore = @keystores.find { |keystore| keystore["name"] == name }
75
76
  KeystoreFactory::create keystore
76
77
  end
77
78
 
data/lib/leeloo/secret.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'gpgme'
2
2
  require 'fileutils'
3
+ require 'digest'
3
4
 
4
5
  module Leeloo
5
6
  class Secret
@@ -26,6 +27,9 @@ module Leeloo
26
27
  # erase the secret
27
28
  end
28
29
 
30
+ def footprint
31
+ # a footprint is a token proving the authenticity of a secret
32
+ end
29
33
  end
30
34
 
31
35
  class LocalFileSystemSecret < Secret
@@ -50,6 +54,13 @@ module Leeloo
50
54
  File.delete @pathname
51
55
  end
52
56
 
57
+ def footprint
58
+ secret = File.read @pathname
59
+ md5 = Digest::MD5.new
60
+ md5 << secret
61
+ md5.hexdigest
62
+ end
63
+
53
64
  end
54
65
 
55
66
  class GpgLocalFileSystemSecret < LocalFileSystemSecret
@@ -88,6 +99,10 @@ module Leeloo
88
99
  @secret.read
89
100
  end
90
101
 
102
+ def footprint
103
+ @secret.footprint
104
+ end
105
+
91
106
  def write phrase
92
107
  @secret.write phrase
93
108
  @git.add @secret.pathname
@@ -0,0 +1,37 @@
1
+ require 'webrick'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ class Server
6
+
7
+ def start preferences
8
+
9
+ puts """
10
+ Please share this url :
11
+ http://your_ip:8000\?q=YOUR_TOKEN
12
+
13
+ run ssh -R:localhost:8000 ssh.localhost.run
14
+ if you want to share your password through tunneling
15
+ """
16
+
17
+ server = WEBrick::HTTPServer.new :Port => 8000
18
+ server.mount_proc '/' do |req, res|
19
+ query = req.query()["q"] || req.body()
20
+ if query
21
+ begin
22
+ body = JSON.parse(Base64.strict_decode64 query)
23
+ key = body["body"] ? JSON.parse(body["body"]) : body
24
+ res.body = preferences.keystore(key["keystore"]).secret_from_footprint(key).read.to_s
25
+ rescue => exception
26
+ puts exception
27
+ res.status = 400
28
+ end
29
+ else
30
+ res.status = 400
31
+ end
32
+ end
33
+
34
+ trap 'INT' do server.shutdown end
35
+ server.start
36
+ end
37
+ end
@@ -1,4 +1,4 @@
1
1
  module Leeloo
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.5.1'.freeze
3
3
  DESCRIPTION = "The easiest way to share securely your secrets".freeze
4
4
  end
data/lib/leeloo.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require 'leeloo/version'
2
+ require 'leeloo/controller'
2
3
  require 'leeloo/command'
3
4
  require 'leeloo/preferences'
4
5
  require 'leeloo/keystore'
5
6
  require 'leeloo/secret'
6
7
  require 'leeloo/output'
8
+ require 'leeloo/server'
7
9
 
8
10
  module Leeloo
9
11
  def self.start
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.3.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - sylvek
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-15 00:00:00.000000000 Z
11
+ date: 2022-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -94,20 +94,34 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webrick
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.7'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.7'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: bundler
99
113
  requirement: !ruby/object:Gem::Requirement
100
114
  requirements:
101
115
  - - "~>"
102
116
  - !ruby/object:Gem::Version
103
- version: '2.0'
117
+ version: '2'
104
118
  type: :development
105
119
  prerelease: false
106
120
  version_requirements: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - "~>"
109
123
  - !ruby/object:Gem::Version
110
- version: '2.0'
124
+ version: '2'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rake
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - "~>"
137
151
  - !ruby/object:Gem::Version
138
152
  version: '3.0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rspec_junit_formatter
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '0.4'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '0.4'
139
167
  description: The easiest way to share securely your secrets
140
168
  email:
141
169
  - smaucourt@gmail.com
@@ -147,16 +175,18 @@ files:
147
175
  - exe/leeloo
148
176
  - lib/leeloo.rb
149
177
  - lib/leeloo/command.rb
178
+ - lib/leeloo/controller.rb
150
179
  - lib/leeloo/keystore.rb
151
180
  - lib/leeloo/output.rb
152
181
  - lib/leeloo/preferences.rb
153
182
  - lib/leeloo/secret.rb
183
+ - lib/leeloo/server.rb
154
184
  - lib/leeloo/version.rb
155
185
  homepage: https://github.com/sylvek/leeloo
156
186
  licenses:
157
187
  - MIT
158
188
  metadata: {}
159
- post_install_message:
189
+ post_install_message:
160
190
  rdoc_options: []
161
191
  require_paths:
162
192
  - lib
@@ -171,8 +201,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
201
  - !ruby/object:Gem::Version
172
202
  version: '0'
173
203
  requirements: []
174
- rubygems_version: 3.0.4
175
- signing_key:
204
+ rubygems_version: 3.3.3
205
+ signing_key:
176
206
  specification_version: 4
177
207
  summary: The easiest way to share securely your secrets
178
208
  test_files: []