leeloo 0.0.16 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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