leeloo 0.3.0 → 0.4.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: f395de66b0d5e6550921e66339d5dcd2df759bc38662d09b4bc650a510cb4dd8
4
- data.tar.gz: 9b311918a29972cd5bca9dc0c856bf37e8bacf59db8b6b954ff1ebd5d4790eeb
3
+ metadata.gz: 45be4ecf01ce08cd7317d3c6f9d8a612d999b972efdcbfe322a964a06f77a303
4
+ data.tar.gz: bd3a8767c2c1d70cdc21856b180cb7f3651c9777471f70a35af810130650d4ef
5
5
  SHA512:
6
- metadata.gz: 36703d60d92d086540d33d16171d1aa290595f4d408db707ac22fdeb11077f8d393ffd861c25aaafa331441586b27b98a8b4f16ac33f61da654c6e27b992ff3f
7
- data.tar.gz: dde8201181fbfb863f091accb97bd2fea285e1cdb7bc75c7ec94ee2c9ce4c1783b8b0c55d593b55b5446506c3826862f32d989657a64873205d4052dbba2ea1b
6
+ metadata.gz: 1ec00409e8ac173872aac71537aad50d40f93a24634b1b101b6f46b59d658696c2480d6e5157fd7cd1d864acc4cc6ec5e421eed6ff74f560c4e25f57ecd7bd27
7
+ data.tar.gz: 1d259f7fc29192bcf6d1cd3a7f7f92d8e9244d79e11741f2fce8f82f8aab0d7cfd5c96ad120c16aa018b8c69540e937e6986a8f18a297a7d137d748e5e19511d
@@ -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
@@ -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
@@ -52,8 +32,7 @@ module Leeloo
52
32
  c.option '--keystore STRING', String, 'a selected keystore'
53
33
 
54
34
  c.action do |args, options|
55
- keystore = @preferences.keystore(options.keystore)
56
- OutputFactory.create(options).render_secrets keystore.secrets
35
+ SecretsController.new(options).display
57
36
  end
58
37
  end
59
38
 
@@ -66,10 +45,9 @@ module Leeloo
66
45
  c.action do |args, options|
67
46
  abort "name is missing" unless args.length == 1
68
47
  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
48
+ ctl = SecretsController.new(options)
49
+ ctl.search(name)
50
+ ctl.display
73
51
  end
74
52
  end
75
53
 
@@ -79,7 +57,7 @@ module Leeloo
79
57
  c.option '--ascii', nil, 'display secrets without unicode tree'
80
58
 
81
59
  c.action do |args, options|
82
- OutputFactory.create(options).render_preferences @preferences
60
+ KeystoreController.new(options).display
83
61
  end
84
62
  end
85
63
 
@@ -87,10 +65,12 @@ module Leeloo
87
65
  c.syntax = 'leeloo keystore remove <name>'
88
66
  c.description = "remove a keystore (path/to/keystore is not destroyed)"
89
67
 
90
- c.action do |args, options|
68
+ c.action do |args, options|args
91
69
  abort "name is missing" unless args.length == 1
92
- @preferences.remove_keystore args.first
93
- OutputFactory.create(options).render_preferences @preferences
70
+ name = args.first
71
+ ctl = KeystoreController.new(options)
72
+ ctl.remove(name)
73
+ ctl.display
94
74
  end
95
75
  end
96
76
 
@@ -100,10 +80,11 @@ module Leeloo
100
80
 
101
81
  c.action do |args, options|
102
82
  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
83
+ name = args.first
84
+ path = args.last
85
+ ctl = KeystoreController.new(options)
86
+ ctl.add(name, path)
87
+ ctl.display
107
88
  end
108
89
  end
109
90
 
@@ -113,9 +94,10 @@ module Leeloo
113
94
 
114
95
  c.action do |args, options|
115
96
  abort "name is missing" unless args.length == 1
116
-
117
- @preferences.set_default_keystore args.first
118
- OutputFactory.create(options).render_preferences @preferences
97
+ name = args.first
98
+ ctl = KeystoreController.new(options)
99
+ ctl.set_default(name)
100
+ ctl.display
119
101
  end
120
102
  end
121
103
 
@@ -129,10 +111,9 @@ module Leeloo
129
111
  c.action do |args, options|
130
112
  abort "name is missing" unless args.length == 1
131
113
  name = args.first
132
-
133
- keystore = @preferences.keystore(options.keystore)
134
- secret = keystore.secret_from_name(name)
135
- OutputFactory.create(options).render_secret secret
114
+ ctl = SecretController.new(options)
115
+ ctl.read(name)
116
+ ctl.display
136
117
  end
137
118
  end
138
119
 
@@ -147,22 +128,9 @@ module Leeloo
147
128
  c.action do |args, options|
148
129
  abort "name is missing" unless args.length == 1
149
130
  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
131
+ ctl = SecretController.new(options)
132
+ ctl.write(name)
133
+ ctl.display
166
134
  end
167
135
  end
168
136
 
@@ -172,9 +140,9 @@ module Leeloo
172
140
  c.option '--keystore STRING', String, 'a selected keystore'
173
141
 
174
142
  c.action do |args, options|
175
- keystore = @preferences.keystore(options.keystore)
176
- text = STDIN.read
177
- OutputFactory.create(options).render_translate keystore, text
143
+ ctl = TranslateController.new(options)
144
+ ctl.translate
145
+ ctl.display
178
146
  end
179
147
  end
180
148
 
@@ -186,10 +154,9 @@ module Leeloo
186
154
  c.action do |args, options|
187
155
  abort "name is missing" unless args.length == 1
188
156
  name = args.first
189
-
190
- keystore = @preferences.keystore(options.keystore)
191
- secret = keystore.secret_from_name(name)
192
- secret.erase
157
+ ctl = SecretController.new(options)
158
+ ctl.remove(name)
159
+ ctl.display
193
160
  end
194
161
  end
195
162
 
@@ -199,8 +166,9 @@ module Leeloo
199
166
  c.option '--keystore STRING', String, 'a selected keystore'
200
167
 
201
168
  c.action do |args, options|
202
- keystore = @preferences.keystore(options.keystore)
203
- keystore.sync
169
+ ctl = KeystoreController.new(options)
170
+ ctl.sync
171
+ ctl.display
204
172
  end
205
173
  end
206
174
 
@@ -210,8 +178,48 @@ module Leeloo
210
178
  c.option '--keystore STRING', String, 'a selected keystore'
211
179
 
212
180
  c.action do |args, options|
213
- keystore = @preferences.keystore(options.keystore)
214
- keystore.init
181
+ ctl = KeystoreController.new(options)
182
+ ctl.init
183
+ ctl.display
184
+ end
185
+ end
186
+
187
+ command :share do |c|
188
+ c.syntax = 'leeloo share <name>'
189
+ c.description = "share a secret with someone"
190
+ c.option '--keystore STRING', String, 'a selected keystore'
191
+
192
+ c.action do |args, options|
193
+ abort "name is missing" unless args.length == 1
194
+ name = args.first
195
+ ctl = ShareController.new(options)
196
+ ctl.token(name)
197
+ ctl.display
198
+ ctl.start_server
199
+ end
200
+ end
201
+
202
+ command :token do |c|
203
+ c.syntax = 'leeloo token <name>'
204
+ c.description = "generate an access token for a given secret"
205
+ c.option '--keystore STRING', String, 'a selected keystore'
206
+
207
+ c.action do |args, options|
208
+ abort "name is missing" unless args.length == 1
209
+ name = args.first
210
+ ctl = ShareController.new(options)
211
+ ctl.token(name)
212
+ ctl.display
213
+ end
214
+ end
215
+
216
+ command :server do |c|
217
+ c.syntax = 'leeloo server'
218
+ c.description = "start a server access token"
219
+
220
+ c.action do |args, options|
221
+ ctl = ShareController.new(options)
222
+ ctl.start_server
215
223
  end
216
224
  end
217
225
 
@@ -0,0 +1,122 @@
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 = @preferences.keystore(@options.keystore).secrets
36
+ end
37
+ def search name
38
+ @secrets = @secrets.select { |secret| secret.name.downcase.include? name.downcase } || []
39
+ end
40
+ def display
41
+ @output.render_secrets @secrets
42
+ end
43
+ end
44
+
45
+ class SecretController < PrivateLocalFileSystemController
46
+ def read name
47
+ @secret = @keystore.secret_from_name(name)
48
+ end
49
+ def write name
50
+ phrase = nil
51
+
52
+ phrase = STDIN.read if @options.stdin
53
+ phrase = SecureRandom.base64(32).truncate(@options.generate.to_i) if @options.generate
54
+
55
+ unless phrase
56
+ phrase = password "secret"
57
+ confirm = password "confirm it"
58
+ abort "not the same secret" unless phrase == confirm
59
+ end
60
+
61
+ @secret = @keystore.secret_from_name(name)
62
+ @secret.write(phrase)
63
+ end
64
+ def remove name
65
+ @secret = @keystore.secret_from_name(name)
66
+ @secret.erase
67
+ end
68
+ def display
69
+ @output.render_secret @secret
70
+ end
71
+ end
72
+
73
+ class TranslateController < PrivateLocalFileSystemController
74
+ def translate
75
+ @text = STDIN.read
76
+ @text.scan(/\$\{.*\}/).each do |secret|
77
+ begin
78
+ @text.gsub! secret, (@keystore.secret_from_name(secret[2..-2])).read.to_s.strip
79
+ rescue => exception
80
+ # silent
81
+ end
82
+ end
83
+ end
84
+ def display
85
+ @output.render_text @text
86
+ end
87
+ end
88
+
89
+ class KeystoreController < PrivateLocalFileSystemController
90
+ def add name, path
91
+ @preferences.add_keystore({"name" => name, "path" => path, "cypher" => "gpg", "vc" => "git"})
92
+ @preferences.keystore(name).init
93
+ end
94
+ def remove name
95
+ @preferences.remove_keystore name
96
+ end
97
+ def set_default name
98
+ @preferences.set_default_keystore name
99
+ end
100
+ def sync
101
+ @keystore.sync
102
+ end
103
+ def init
104
+ @keystore.init
105
+ end
106
+ def display
107
+ @output.render_preferences @preferences
108
+ end
109
+ end
110
+
111
+ class ShareController < PrivateLocalFileSystemController
112
+ def token name
113
+ @footprint = @keystore.footprint_of(name)
114
+ end
115
+ def start_server
116
+ Server.new.start @preferences
117
+ end
118
+ def display
119
+ @output.render_footprint @footprint
120
+ end
121
+ end
122
+ 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
@@ -92,6 +101,19 @@ module Leeloo
92
101
  secret_of "#{path}/secrets/#{name}"
93
102
  end
94
103
 
104
+ def footprint_of name
105
+ secret = secret_from_name name
106
+ { "footprint" => secret.footprint, "keystore" => self.name, "secret" => secret.name }
107
+ end
108
+
109
+ def secret_from_footprint footprint
110
+ secret = secret_from_name footprint["secret"]
111
+ unless secret.footprint == footprint["footprint"]
112
+ raise "footprint is not valid"
113
+ end
114
+ secret
115
+ end
116
+
95
117
  end
96
118
 
97
119
  class GpgPrivateLocalFileSystemKeystore < PrivateLocalFileSystemKeystore
@@ -119,6 +141,20 @@ module Leeloo
119
141
  secret_of "#{path}/secrets/#{name}.gpg"
120
142
  end
121
143
 
144
+ def footprint_of name
145
+ footprint = super name
146
+ footprint["sign"] = Base64.strict_encode64 GPGME::Crypto.new.sign(footprint["footprint"]).to_s
147
+ footprint
148
+ end
149
+
150
+ def secret_from_footprint footprint
151
+ data = GPGME::Crypto.new.verify(Base64.strict_decode64 footprint["sign"]) { |signature| signature.valid? }
152
+ if data.read == footprint["footprint"]
153
+ super footprint
154
+ else
155
+ raise "signature is not valid"
156
+ end
157
+ end
122
158
  end
123
159
 
124
160
  class GitKeystoreDecorator < Keystore
@@ -148,6 +184,14 @@ module Leeloo
148
184
  @keystore.path
149
185
  end
150
186
 
187
+ def footprint_of element
188
+ @keystore.footprint_of element
189
+ end
190
+
191
+ def secret_from_footprint footprint
192
+ @keystore.secret_from_footprint footprint
193
+ end
194
+
151
195
  def sync
152
196
  @git.pull
153
197
  @keystore.sync
@@ -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,13 @@ 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_footprint footprint
25
+ end
26
+
27
+ def render_share footprint
19
28
  end
20
29
  end
21
30
 
@@ -37,16 +46,14 @@ module Leeloo
37
46
  end
38
47
  end
39
48
 
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
49
+ def render_text text
48
50
  puts text
49
51
  end
52
+
53
+ def render_footprint footprint
54
+ puts "token:"
55
+ puts Base64.strict_encode64 footprint.to_json
56
+ end
50
57
  end
51
58
 
52
59
  class Terminal < Ascii
@@ -107,16 +114,16 @@ module Leeloo
107
114
  @output.render_secrets secrets
108
115
  end
109
116
 
110
- def render_translate keystore, text
111
- @output.render_translate keystore, text
117
+ def render_text text
118
+ @output.render_text text
112
119
  end
113
120
 
114
121
  def render_secret secret
115
122
 
116
123
  Signal.trap("INT") do
117
124
  Clipboard.clear
118
- abort "ciao"
119
- end
125
+ abort "cleared"
126
+ end
120
127
 
121
128
  Clipboard.copy secret.read
122
129
  wait = Thread.new do
@@ -129,6 +136,14 @@ module Leeloo
129
136
  wait.join
130
137
  Clipboard.clear
131
138
  end
139
+
140
+ def render_footprint footprint
141
+ @output.render_footprint footprint
142
+ end
143
+
144
+ def render_share footprint
145
+ @output.render_share footprint
146
+ end
132
147
  end
133
148
 
134
149
  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
 
@@ -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.4.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.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sylvek
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-15 00:00:00.000000000 Z
11
+ date: 2019-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: commander
@@ -147,10 +147,12 @@ files:
147
147
  - exe/leeloo
148
148
  - lib/leeloo.rb
149
149
  - lib/leeloo/command.rb
150
+ - lib/leeloo/controller.rb
150
151
  - lib/leeloo/keystore.rb
151
152
  - lib/leeloo/output.rb
152
153
  - lib/leeloo/preferences.rb
153
154
  - lib/leeloo/secret.rb
155
+ - lib/leeloo/server.rb
154
156
  - lib/leeloo/version.rb
155
157
  homepage: https://github.com/sylvek/leeloo
156
158
  licenses: