ruby-keepassx 0.2.0beta11
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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +9 -0
- data/.rvmrc +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +5 -0
- data/README.md +28 -0
- data/Rakefile +5 -0
- data/lib/keepassx.rb +56 -0
- data/lib/keepassx/aes_crypt.rb +19 -0
- data/lib/keepassx/database.rb +431 -0
- data/lib/keepassx/entry.rb +155 -0
- data/lib/keepassx/entry_field.rb +27 -0
- data/lib/keepassx/exceptions.rb +4 -0
- data/lib/keepassx/field.rb +205 -0
- data/lib/keepassx/group.rb +138 -0
- data/lib/keepassx/group_field.rb +22 -0
- data/lib/keepassx/header.rb +178 -0
- data/lib/keepassx/item.rb +128 -0
- data/lib/keepassx/utilities.rb +226 -0
- data/ruby-keepassx.gemspec +23 -0
- data/spec/fixtures/test_data_array.yaml +54 -0
- data/spec/fixtures/test_data_hash.yaml +36 -0
- data/spec/fixtures/test_database.kdb +0 -0
- data/spec/keepassx/database_spec.rb +338 -0
- data/spec/keepassx/entry_spec.rb +72 -0
- data/spec/keepassx/group_spec.rb +47 -0
- data/spec/spec_helper.rb +64 -0
- metadata +126 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'ruby-keepassx'
|
3
|
+
s.summary = 'Ruby API access for KeePassX databases'
|
4
|
+
s.description = 'This is fork of Tony Pitluga\'s ' \
|
5
|
+
'Ruby API for keepassx with read-write support.'
|
6
|
+
s.version = '0.2.0beta11'
|
7
|
+
s.authors = ['Tony Pitluga', 'Paul Hinze', 'Tio Teath']
|
8
|
+
s.email = ['tony.pitluga@gmail.com', 'paul.t.hinze@gmail.com',
|
9
|
+
'tio.teath@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/tioteath/ruby-keepassx.git'
|
11
|
+
s.files = `git ls-files`.split "\n"
|
12
|
+
|
13
|
+
# TODO: This won't work, figure out why.
|
14
|
+
if RUBY_VERSION =~ /1\.8/
|
15
|
+
s.add_dependency 'backports', '~> 3.6.0'
|
16
|
+
end
|
17
|
+
|
18
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
19
|
+
s.add_development_dependency 'pry', '~> 0.9.12'
|
20
|
+
s.add_development_dependency 'rake', '~> 0.8', '>= 0.8.7'
|
21
|
+
# s.add_development_dependency 'respect', '~> 0.1.1'
|
22
|
+
# s.add_development_dependency 'rspec-prof', '~> 0'
|
23
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
---
|
2
|
+
- :title: :Internet
|
3
|
+
:icon: 1
|
4
|
+
:groups:
|
5
|
+
- :title: :Web
|
6
|
+
:icon: 61
|
7
|
+
:groups:
|
8
|
+
- :title: :Wikipedia
|
9
|
+
:icon: 54
|
10
|
+
:groups:
|
11
|
+
- :title: :Amazon
|
12
|
+
:icon: 30
|
13
|
+
:entries:
|
14
|
+
- :title: test entry2
|
15
|
+
:username: testuser
|
16
|
+
:url: http://example.com/testurl
|
17
|
+
:password: testpassword
|
18
|
+
:comment: test comment
|
19
|
+
:group: :Web
|
20
|
+
:uuid: f720371d-15f1-4b9b-918e-68bb83e4407e
|
21
|
+
:lastmod: 2008-09-07 23:40:43
|
22
|
+
:lastaccess: 2008-09-07 23:40:43
|
23
|
+
:creation: 2008-09-07 23:40:43
|
24
|
+
- :title: test entry3
|
25
|
+
:username: testuser3
|
26
|
+
:url: http://example.com/testurl3
|
27
|
+
:password: testpassword3
|
28
|
+
:comment: test comment3
|
29
|
+
:group: :Web
|
30
|
+
:uuid: e720371d-15f1-4c9b-918e-68bb83e4407e
|
31
|
+
:lastmod: 2008-09-07 23:43:43
|
32
|
+
:lastaccess: 2008-09-07 23:20:43
|
33
|
+
:creation: 2008-09-07 23:50:43
|
34
|
+
:entries:
|
35
|
+
- :title: test entry
|
36
|
+
:username: testuser
|
37
|
+
:url: http://example.com/testurl
|
38
|
+
:password: testpassword
|
39
|
+
:comment: test comment
|
40
|
+
:uuid: 2d3fae36-e89f-48bd-8900-389cc01ef248
|
41
|
+
:lastmod: 2008-09-03 23:40:43
|
42
|
+
:lastaccess: 2008-09-03 23:40:43
|
43
|
+
:creation: 2008-09-03 23:40:43
|
44
|
+
- :title: entry2
|
45
|
+
:username: user
|
46
|
+
:url: http://example.com
|
47
|
+
:password: pass2
|
48
|
+
:comment: comment
|
49
|
+
:uuid: 866216ea-3a57-43df-8847-becfc3593939
|
50
|
+
:lastmod: 2008-09-07 22:40:43
|
51
|
+
:lastaccess: 2008-09-07 22:40:43
|
52
|
+
:creation: 2008-09-07 22:40:43
|
53
|
+
- :title: :eMail
|
54
|
+
:icon: 19
|
@@ -0,0 +1,36 @@
|
|
1
|
+
:path: /tmp/new_database.kdb
|
2
|
+
:groups:
|
3
|
+
- :title: :Internet
|
4
|
+
:icon: 1
|
5
|
+
- :title: :Web
|
6
|
+
:icon: 61
|
7
|
+
:parent: :Internet
|
8
|
+
- :title: :Wikipedia
|
9
|
+
:icon: 54
|
10
|
+
:parent: :Web
|
11
|
+
- :title: :eMail
|
12
|
+
:icon: 19
|
13
|
+
:entries:
|
14
|
+
- :title: test entry2
|
15
|
+
:username: testuser
|
16
|
+
:url: http://example.com/testurl
|
17
|
+
:password: testpassword
|
18
|
+
:comment: test comment
|
19
|
+
:group: :Wikipedia
|
20
|
+
- :title: test entry
|
21
|
+
:username: testuser
|
22
|
+
:url: http://example.com/testurl
|
23
|
+
:password: testpassword
|
24
|
+
:comment: test comment
|
25
|
+
:group: :Internet
|
26
|
+
- :title: test entry
|
27
|
+
:username: testuser
|
28
|
+
:url: http://example.com/testurl
|
29
|
+
:password: testpassword
|
30
|
+
:comment: test comment
|
31
|
+
:group: :Internet
|
32
|
+
- :title: entry2
|
33
|
+
:username: user
|
34
|
+
:url: http://example.com
|
35
|
+
:password: pass2
|
36
|
+
:comment: comment
|
Binary file
|
@@ -0,0 +1,338 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
# require 'ruby-prof'
|
3
|
+
## FIXME: Add exception tests.
|
4
|
+
describe Keepassx::Database do
|
5
|
+
|
6
|
+
include_context :keepassx
|
7
|
+
|
8
|
+
describe '#open' do
|
9
|
+
it 'creates a new instance of the databse with the file' do
|
10
|
+
expect(test_db).to_not be nil
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
describe '#new' do
|
16
|
+
subject { Keepassx }
|
17
|
+
let(:db1) { subject.new data_array }
|
18
|
+
let(:db2) { subject.new data_array }
|
19
|
+
|
20
|
+
it 'has the same checksum for the same data' do
|
21
|
+
expect(db1.checksum).to eq db2.checksum
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
describe '#unlock' do
|
27
|
+
|
28
|
+
it 'returns false when the master password is incorrect' do
|
29
|
+
expect(test_db.unlock('bad password')).to be false
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
it 'returns true when the master password is correct' do
|
34
|
+
expect(test_db.unlock('testmasterpassword')).to be true
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
describe '#locked?' do
|
41
|
+
it 'returns false when unlocked' do
|
42
|
+
expect(test_db.locked?).to be false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
context 'unlocked database' do
|
48
|
+
|
49
|
+
it 'can find entries by their title' do
|
50
|
+
# FIXME: Add more field tests
|
51
|
+
expect(test_db.entry(:title => 'test entry').password).to eq 'testpassword'
|
52
|
+
expect(test_db.entry(:title => 'test entry').creation).to eq Time.
|
53
|
+
local 2011, 9, 3, 15, 34, 47
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
it 'can find groups' do
|
58
|
+
expect(test_db.groups.map(&:title).sort).
|
59
|
+
to eq ['Backup', 'Internet', 'Web', 'Wikipedia', 'eMail']
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
it 'has "Internet" group level properly set' do
|
64
|
+
expect(test_db.group(:Internet).level).to eq 0
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
it 'has "Internet" group parent properly set' do
|
69
|
+
expect(test_db.group(:Internet).parent).to be nil
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
it 'has "Web" group level properly set' do
|
74
|
+
expect(test_db.group(:Web).level).to eq 1
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
it 'has "Web" group parent properly set' do
|
79
|
+
expect(test_db.group(:Web).parent).to be test_db.group(:Internet)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
it 'has "eMail" group level properly set' do
|
84
|
+
expect(test_db.group(:eMail).level).to eq 0
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
it 'has "eMail" group parent properly set' do
|
89
|
+
expect(test_db.group(:eMail).parent).to be nil
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
it 'can search for entries' do
|
94
|
+
expect(test_db.search('test').first.title).to eq 'test entry'
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
it 'can search for entries case-insensitively' do
|
99
|
+
expect(test_db.search('TEST').first.title).to eq 'test entry'
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
it 'will find the current values of entries with history' do
|
104
|
+
entries = test_db.search 'entry2'
|
105
|
+
expect(entries.size).to be 1
|
106
|
+
expect(entries.first.title).to eq 'entry2'
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
it 'can add and delete groups and entries' do
|
111
|
+
expect { @group = test_db.add :group, :title => 'group3' }.to_not raise_error
|
112
|
+
|
113
|
+
expect { @entry = Keepassx::Entry.new(
|
114
|
+
:title => 'entry3',
|
115
|
+
:group => @group
|
116
|
+
) }.to_not raise_error
|
117
|
+
|
118
|
+
expect { test_db.add(@entry) }.to_not raise_error
|
119
|
+
expect(test_db.entry :title => 'entry3', :group => @group).to_not be nil
|
120
|
+
|
121
|
+
expect { @entry = Keepassx::Entry.new(
|
122
|
+
:title => 'entry4',
|
123
|
+
:group => @group
|
124
|
+
) }.to_not raise_error
|
125
|
+
expect { test_db.add(@entry) }.to_not raise_error
|
126
|
+
expect(test_db.entry :title => 'entry4', :group => @group).to_not be nil
|
127
|
+
|
128
|
+
expect(test_db.entries(:group => @group).length).to eq 2
|
129
|
+
|
130
|
+
expect(test_db.delete(:group, :title => 'group3').class).to be Keepassx::Group
|
131
|
+
expect(test_db.entries :group => @group).to eq []
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
it 'can be exported to XML' do
|
136
|
+
expect { @xml = test_db.to_xml }.to_not raise_error
|
137
|
+
expect(@xml.doctype.name).to eq 'KEEPASSX_DATABASE'
|
138
|
+
expect(@xml.root.name).to eq 'database'
|
139
|
+
# require 'rexml/formatters/pretty'
|
140
|
+
# formatter = REXML::Formatters::Pretty.new 2
|
141
|
+
# formatter.compact = true # This is the magic line that does what you need!
|
142
|
+
# formatter.write @xml, $stderr
|
143
|
+
# formatter.write @xml, File.new('/tmp/keepass.xml', 'w+')
|
144
|
+
# warn @xml
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
context 'new database' do
|
151
|
+
|
152
|
+
it 'is properly initialized' do
|
153
|
+
expect(new_db.valid?).to be true
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# Unlocked by default for new container
|
158
|
+
it 'is unlocked' do
|
159
|
+
expect(new_db.locked?).to be false
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
it 'has non-empty checksum ' do
|
164
|
+
expect(test_db.checksum).to_not be nil
|
165
|
+
expect(test_db.checksum).to_not be ''
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
it 'can add groups and entries' do
|
170
|
+
[
|
171
|
+
{ :title => :Internet, :icon => 1 },
|
172
|
+
{ :title => :eMail, :icon => 19 },
|
173
|
+
{ :title => :Web, :parent => :Internet, :icon => 61 },
|
174
|
+
{ :title => :Wikipedia, :parent => :Web, :icon => 54 }
|
175
|
+
].each do |opts|
|
176
|
+
expect { new_db.add(:group, opts) }.to_not raise_error
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
it 'contains group "eMail" with proper index value' do
|
182
|
+
expect(new_db.index new_db.group(:eMail)).to eq 3
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
it 'contains group "eMail" with proper level value' do
|
187
|
+
expect(new_db.group(:eMail).level).to eq 0
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
it 'contains group "Web" with proper level value' do
|
192
|
+
expect(new_db.index new_db.group(:Web)).to eq 1
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
it 'contains group "Web" with proper level value' do
|
197
|
+
expect(new_db.group(:Web).level).to eq 1
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
it 'contains group "Wikipedia" with proper level value' do
|
202
|
+
expect(new_db.index new_db.group(:Wikipedia)).to eq 2
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'contains group "Wikipedia" with proper level value' do
|
206
|
+
expect(new_db.group(:Wikipedia).level).to eq 2
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
it 'adds new entry "test entry"' do
|
211
|
+
# FIXME: There's some value set for binary_data field
|
212
|
+
expect { new_db.add(:entry,
|
213
|
+
:title => 'test entry', :username => 'testuser',
|
214
|
+
:url => 'http://example.com/testurl', :password => 'testpassword',
|
215
|
+
:comment => 'test comment', :group => :Internet)
|
216
|
+
}.to_not raise_error
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
it 'adds new entry "entry2"' do
|
221
|
+
expect { new_db.add(:entry,
|
222
|
+
:title => 'entry2', :username => 'user', :url => 'http://example.com',
|
223
|
+
:password => 'pass2', :comment => 'comment',
|
224
|
+
:group => :Internet)
|
225
|
+
}.to_not raise_error
|
226
|
+
end
|
227
|
+
|
228
|
+
|
229
|
+
it 'adds new entry "test entry2"' do
|
230
|
+
expect { new_db.add(:entry,
|
231
|
+
:title => 'test entry2', :username => 'testuser',
|
232
|
+
:url => 'http://example.com/testurl', :password => 'testpassword',
|
233
|
+
:comment => 'test comment', :group => :Web)
|
234
|
+
}.to_not raise_error
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
it 'does not change checksum after save' do
|
239
|
+
@new_db_checksum = @new_db.checksum
|
240
|
+
expect { new_db.save 'testmasterpassword' }.to_not raise_error
|
241
|
+
expect(new_db.checksum).to eq @new_db_checksum
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
it 'saves checksum properly' do
|
246
|
+
expect(new_db.header.contents_hash).to eq new_db.checksum
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
it 'can delete entry' do
|
251
|
+
group = new_db.add(:group, :title => 'Test Group 1')
|
252
|
+
new_db.add_entry(:title => 'Test Entry 1', :group => group)
|
253
|
+
|
254
|
+
expect {new_db.delete group}.to_not raise_error
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
it 'matches reference database' do
|
259
|
+
# expect(new_db = Keepassx::Database.open(NEW_DATABASE_PATH)).to_not be nil
|
260
|
+
expect(new_db.header.group_number).to eq 4
|
261
|
+
|
262
|
+
# FIXME: Delete functionality seems not to work properly, should be 3 here
|
263
|
+
expect(new_db.header.entry_number).to eq 4 # TODO: Don't forget about meta entries
|
264
|
+
expect(new_db.unlock 'testmasterpassword').to be true
|
265
|
+
|
266
|
+
# expect(new_db.index new_db.group(:Backup)).to eq 4
|
267
|
+
# expect(new_db.group(:Backup).level).to eq 0
|
268
|
+
|
269
|
+
new_db.groups.each do |group|
|
270
|
+
expect(group.title).to eq test_db.group(:title => group.title).title
|
271
|
+
expect(group.level).to eq test_db.group(group.title).level
|
272
|
+
end
|
273
|
+
|
274
|
+
# TODO: Implement entries verification using reference database
|
275
|
+
# new_db.entries.each do |entry|
|
276
|
+
# expect(entry.username).to eq test_db.entry(:title => entry.title).username
|
277
|
+
# expect(entry.group.title).to eq test_db.entry(entry.title).
|
278
|
+
# group.title
|
279
|
+
# end
|
280
|
+
expect { new_db.save 'testmasterpassword' }.to_not raise_error
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
end
|
285
|
+
|
286
|
+
context 'new database from array' do
|
287
|
+
|
288
|
+
it 'properly initialized from Array' do
|
289
|
+
expect { data_array_db }.to_not raise_error
|
290
|
+
end
|
291
|
+
|
292
|
+
|
293
|
+
it 'has grup_number counter properly set' do
|
294
|
+
expect(data_array_db.header.group_number).to eq 5
|
295
|
+
end
|
296
|
+
|
297
|
+
|
298
|
+
it 'contains proper number of test groups' do
|
299
|
+
expect(data_array_db.groups.length).to eq 5
|
300
|
+
end
|
301
|
+
|
302
|
+
|
303
|
+
it 'contains proper number of test entries' do
|
304
|
+
expect(data_array_db.entries.length).to eq 4
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
it 'has entry counter properly set' do
|
309
|
+
expect(data_array_db.header.entry_number).to eq 4
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
it 'preserves original Array' do
|
314
|
+
expect(data_array).to eq original_data_array
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
|
320
|
+
describe '#to_a' do
|
321
|
+
|
322
|
+
let :database_schema do
|
323
|
+
# FIXME: Figure out how to properly define schema for nested Array
|
324
|
+
Respect::ArraySchema.define do |s|
|
325
|
+
s.item do |i|
|
326
|
+
# i.hash
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'returns Array database representation' do
|
332
|
+
# expect(database_schema.validate? data_array_db.to_a).to be true
|
333
|
+
expect(data_array_db.to_a.class).to be Array
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|