keepassx 0.1.0 → 1.0.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 +7 -0
- data/.codeclimate.yml +30 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +64 -0
- data/.travis.yml +12 -3
- data/Gemfile +4 -2
- data/Guardfile +16 -0
- data/LICENSE +19 -0
- data/README.md +33 -0
- data/Rakefile +3 -2
- data/keepassx.gemspec +20 -10
- data/lib/keepassx.rb +42 -3
- data/lib/keepassx/aes_crypt.rb +16 -6
- data/lib/keepassx/database.rb +218 -27
- data/lib/keepassx/database/dumper.rb +87 -0
- data/lib/keepassx/database/finder.rb +102 -0
- data/lib/keepassx/database/loader.rb +217 -0
- data/lib/keepassx/entry.rb +70 -38
- data/lib/keepassx/field/base.rb +191 -0
- data/lib/keepassx/field/entry.rb +32 -0
- data/lib/keepassx/field/group.rb +27 -0
- data/lib/keepassx/fieldable.rb +161 -0
- data/lib/keepassx/group.rb +93 -20
- data/lib/keepassx/hashable_payload.rb +6 -0
- data/lib/keepassx/header.rb +102 -27
- data/lib/keepassx/version.rb +5 -0
- data/spec/factories.rb +23 -0
- data/spec/fixtures/database_empty.kdb +0 -0
- data/spec/fixtures/database_test.kdb +0 -0
- data/spec/fixtures/database_test_dumped.yml +76 -0
- data/spec/fixtures/database_with_key.kdb +0 -0
- data/spec/fixtures/database_with_key.key +1 -0
- data/spec/fixtures/database_with_key2.key +1 -0
- data/spec/fixtures/test_data_array.yml +113 -0
- data/spec/fixtures/test_data_array_dumped.yml +124 -0
- data/spec/keepassx/database_spec.rb +491 -29
- data/spec/keepassx/entry_spec.rb +95 -0
- data/spec/keepassx/group_spec.rb +92 -0
- data/spec/keepassx_spec.rb +17 -0
- data/spec/spec_helper.rb +59 -3
- metadata +143 -69
- data/.rvmrc +0 -1
- data/Gemfile.lock +0 -28
- data/lib/keepassx/entry_field.rb +0 -49
- data/lib/keepassx/group_field.rb +0 -44
- data/spec/test_database.kdb +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4db73a09546a08ab55cb858af6633140b1330b29512f67b2fc535dfc7faa497d
|
4
|
+
data.tar.gz: 07ce161d5e70797499e7fc91c16ad6e4155e39ec38088ea615ca4e1adddf35e7
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8b02a38627723697feda5d948e8a678840a4fbfefe035da2b53ce745bc4041ee3b18d933cefa313943a5710b30d5162b1a4037c13eb00ce6db9fb6127c361d71
|
7
|
+
data.tar.gz: b5724c17a5f53249ce6a54e48fbe99f9d4282ed017ce15b12ea4174afa5d7e4abef05096280bd7020afb948b881e3135b929b6e85ed4483a5167ceff0d4ac706
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
exclude_fingerprints:
|
6
|
+
# dumper.rb
|
7
|
+
- dec843d4724d26c64427d1037a2ea4f1
|
8
|
+
# aes_crypt.rb
|
9
|
+
- 33893778c027f4ce2aaef293c8eb00e0
|
10
|
+
config:
|
11
|
+
languages:
|
12
|
+
- ruby
|
13
|
+
- javascript
|
14
|
+
- python
|
15
|
+
- php
|
16
|
+
fixme:
|
17
|
+
enabled: true
|
18
|
+
rubocop:
|
19
|
+
enabled: true
|
20
|
+
ratings:
|
21
|
+
paths:
|
22
|
+
- "**.inc"
|
23
|
+
- "**.js"
|
24
|
+
- "**.jsx"
|
25
|
+
- "**.module"
|
26
|
+
- "**.php"
|
27
|
+
- "**.py"
|
28
|
+
- "**.rb"
|
29
|
+
exclude_paths:
|
30
|
+
- spec/
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
Exclude:
|
4
|
+
- spec/**/*
|
5
|
+
|
6
|
+
Documentation:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
Layout/AlignHash:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Layout/EmptyLines:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Layout/EmptyLinesAroundClassBody:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Layout/EmptyLinesAroundBlockBody:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Layout/EmptyLinesAroundModuleBody:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Layout/EmptyLineBetweenDefs:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
Layout/IndentationConsistency:
|
28
|
+
EnforcedStyle: rails
|
29
|
+
|
30
|
+
Metrics/AbcSize:
|
31
|
+
Enabled: false
|
32
|
+
|
33
|
+
Metrics/LineLength:
|
34
|
+
Enabled: false
|
35
|
+
|
36
|
+
Metrics/MethodLength:
|
37
|
+
Max: 12
|
38
|
+
|
39
|
+
Metrics/ModuleLength:
|
40
|
+
Max: 160
|
41
|
+
|
42
|
+
Metrics/ClassLength:
|
43
|
+
Max: 160
|
44
|
+
|
45
|
+
Metrics/CyclomaticComplexity:
|
46
|
+
Enabled: false
|
47
|
+
|
48
|
+
Metrics/PerceivedComplexity:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
Naming/AccessorMethodName:
|
52
|
+
Enabled: false
|
53
|
+
|
54
|
+
Style/TrailingCommaInArrayLiteral:
|
55
|
+
EnforcedStyleForMultiline: comma
|
56
|
+
|
57
|
+
Style/TrailingCommaInHashLiteral:
|
58
|
+
EnforcedStyleForMultiline: comma
|
59
|
+
|
60
|
+
Style/NumericPredicate:
|
61
|
+
EnforcedStyle: comparison
|
62
|
+
|
63
|
+
Style/UnpackFirst:
|
64
|
+
Enabled: false
|
data/.travis.yml
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
sudo: false
|
2
4
|
rvm:
|
3
|
-
-
|
4
|
-
-
|
5
|
-
-
|
5
|
+
- 2.6.5
|
6
|
+
- 2.5.7
|
7
|
+
- 2.4.9
|
8
|
+
- 2.3.8
|
9
|
+
- jruby-9.2.8.0
|
10
|
+
before_install:
|
11
|
+
- gem update --system
|
12
|
+
- gem install bundler --no-document
|
13
|
+
after_success:
|
14
|
+
- bundle exec codeclimate-test-reporter
|
data/Gemfile
CHANGED
data/Guardfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
4
|
+
require 'guard/rspec/dsl'
|
5
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
ruby = dsl.ruby
|
15
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
16
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -0,0 +1,33 @@
|
|
1
|
+
## Keepassx
|
2
|
+
|
3
|
+
[](https://github.com/pitluga/keepassx/blob/master/LICENSE)
|
4
|
+
[](https://github.com/pitluga/keepassx/releases/latest)
|
5
|
+
[](https://travis-ci.org/pitluga/keepassx)
|
6
|
+
|
7
|
+
### A Ruby library to read and write [KeePassX](http://www.keepassx.org/) databases.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
```sh
|
12
|
+
gem install keepassx
|
13
|
+
```
|
14
|
+
|
15
|
+
or if you use bundler
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
gem 'keepassx'
|
19
|
+
```
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'keepassx'
|
25
|
+
|
26
|
+
database = Keepassx::Database.open("/path/to/database.kdb")
|
27
|
+
database.unlock("the master password")
|
28
|
+
puts database.entry("entry's title").password
|
29
|
+
```
|
30
|
+
|
31
|
+
## Security Warning
|
32
|
+
|
33
|
+
No attempt is made to protect the memory used by this library; there may be something we can do with libgcrypt's secure-malloc functions, but right now your master password is unencrypted in ram that could possibly be paged to disk.
|
data/Rakefile
CHANGED
data/keepassx.gemspec
CHANGED
@@ -1,14 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/keepassx/version'
|
4
|
+
|
1
5
|
Gem::Specification.new do |s|
|
2
|
-
s.name
|
3
|
-
s.
|
4
|
-
s.
|
5
|
-
s.
|
6
|
-
s.
|
7
|
-
s.
|
8
|
-
s.
|
9
|
-
s.
|
6
|
+
s.name = 'keepassx'
|
7
|
+
s.version = Keepassx::VERSION
|
8
|
+
s.authors = ['Tony Pitluga', 'Paul Hinze']
|
9
|
+
s.email = ['tony.pitluga@gmail.com', 'paul.t.hinze@gmail.com']
|
10
|
+
s.homepage = 'http://github.com/pitluga/keepassx'
|
11
|
+
s.summary = 'Ruby API access for KeePassX databases'
|
12
|
+
s.description = 'See http://github.com/pitluga/keepassx'
|
13
|
+
s.license = 'MIT'
|
10
14
|
|
11
|
-
s.
|
15
|
+
s.files = `git ls-files`.split("\n")
|
12
16
|
|
13
|
-
s.add_development_dependency
|
17
|
+
s.add_development_dependency 'factory_bot'
|
18
|
+
s.add_development_dependency 'guard'
|
19
|
+
s.add_development_dependency 'guard-rspec'
|
20
|
+
s.add_development_dependency 'rake', '~> 10.4'
|
21
|
+
s.add_development_dependency 'respect'
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'simplecov'
|
14
24
|
end
|
data/lib/keepassx.rb
CHANGED
@@ -1,13 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
require 'stringio'
|
3
5
|
require 'openssl'
|
6
|
+
require 'securerandom'
|
4
7
|
require 'digest/sha2'
|
5
|
-
require '
|
8
|
+
require 'yaml'
|
6
9
|
|
10
|
+
require 'keepassx/database/dumper'
|
11
|
+
require 'keepassx/database/loader'
|
12
|
+
require 'keepassx/database/finder'
|
7
13
|
require 'keepassx/database'
|
14
|
+
require 'keepassx/field/base'
|
15
|
+
require 'keepassx/field/entry'
|
16
|
+
require 'keepassx/field/group'
|
17
|
+
require 'keepassx/fieldable'
|
8
18
|
require 'keepassx/entry'
|
9
|
-
require 'keepassx/entry_field'
|
10
19
|
require 'keepassx/group'
|
11
|
-
require 'keepassx/group_field'
|
12
20
|
require 'keepassx/header'
|
13
21
|
require 'keepassx/aes_crypt'
|
22
|
+
|
23
|
+
module Keepassx
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# Create Keepassx database
|
27
|
+
#
|
28
|
+
# @param opts [Hash] Keepassx database options.
|
29
|
+
# @yield [opts]
|
30
|
+
# @yieldreturn [Fixnum]
|
31
|
+
# @return [Keepassx::Database]
|
32
|
+
def new(opts)
|
33
|
+
db = Database.new(opts)
|
34
|
+
yield db if block_given?
|
35
|
+
db
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
# Read Keepassx database from file storage.
|
40
|
+
#
|
41
|
+
# @param opts [Hash] Keepassx database options.
|
42
|
+
# @yield [opts]
|
43
|
+
# @yieldreturn [Fixnum]
|
44
|
+
# @return [Keepassx::Database]
|
45
|
+
def open(opts)
|
46
|
+
db = Database.new(opts)
|
47
|
+
yield db if block_given?
|
48
|
+
db
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/lib/keepassx/aes_crypt.rb
CHANGED
@@ -1,19 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Keepassx
|
2
4
|
module AESCrypt
|
3
|
-
|
4
|
-
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
8
|
+
def decrypt(encrypted_data, key, iv, cipher_type)
|
9
|
+
aes = OpenSSL::Cipher.new(cipher_type)
|
5
10
|
aes.decrypt
|
6
11
|
aes.key = key
|
7
|
-
aes.iv
|
12
|
+
aes.iv = iv unless iv.nil?
|
8
13
|
aes.update(encrypted_data) + aes.final
|
9
14
|
end
|
15
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
10
16
|
|
11
|
-
|
12
|
-
|
17
|
+
|
18
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
19
|
+
def encrypt(data, key, iv, cipher_type)
|
20
|
+
aes = OpenSSL::Cipher.new(cipher_type)
|
13
21
|
aes.encrypt
|
14
22
|
aes.key = key
|
15
|
-
aes.iv
|
23
|
+
aes.iv = iv unless iv.nil?
|
16
24
|
aes.update(data) + aes.final
|
17
25
|
end
|
26
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
27
|
+
|
18
28
|
end
|
19
29
|
end
|
data/lib/keepassx/database.rb
CHANGED
@@ -1,45 +1,236 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Keepassx
|
2
4
|
class Database
|
3
5
|
|
4
|
-
|
6
|
+
include Database::Dumper
|
7
|
+
include Database::Loader
|
8
|
+
include Database::Finder
|
5
9
|
|
6
|
-
def self.open(path)
|
7
|
-
content = File.respond_to?(:binread) ? File.binread(path) : File.read(path)
|
8
|
-
self.new(content)
|
9
|
-
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# Check database validity.
|
12
|
+
#
|
13
|
+
# @return [Boolean]
|
14
|
+
def valid?
|
15
|
+
header.valid?
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
+
|
19
|
+
# Get lock state
|
20
|
+
#
|
21
|
+
# @return [Boolean]
|
22
|
+
def locked?
|
23
|
+
@locked
|
18
24
|
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
|
27
|
+
# Add new group to database.
|
28
|
+
#
|
29
|
+
# @param opts [Hash] Options that will be passed to Keepassx::Group#new.
|
30
|
+
# @return [Keepassx::Group]
|
31
|
+
# rubocop:disable Metrics/MethodLength
|
32
|
+
def add_group(opts)
|
33
|
+
raise ArgumentError, "Expected Hash or Keepassx::Group, got #{opts.class}" unless valid_group?(opts)
|
34
|
+
|
35
|
+
if opts.is_a?(Keepassx::Group)
|
36
|
+
# Assign parent group
|
37
|
+
parent = opts.parent
|
38
|
+
index = last_sibling_index(parent) + 1
|
39
|
+
@groups.insert(index, opts)
|
40
|
+
|
41
|
+
# Increment counter
|
42
|
+
header.groups_count += 1
|
43
|
+
|
44
|
+
# Return group
|
45
|
+
opts
|
46
|
+
|
47
|
+
elsif opts.is_a?(Hash)
|
48
|
+
opts = deep_copy(opts)
|
49
|
+
opts = build_group_options(opts)
|
50
|
+
|
51
|
+
# Create group
|
52
|
+
group = create_group(opts)
|
53
|
+
|
54
|
+
# Increment counter
|
55
|
+
header.groups_count += 1
|
56
|
+
|
57
|
+
# Return group
|
58
|
+
group
|
59
|
+
end
|
29
60
|
end
|
61
|
+
# rubocop:enable Metrics/MethodLength
|
62
|
+
|
63
|
+
|
64
|
+
# Add new entry to database.
|
65
|
+
#
|
66
|
+
# @param opts [Hash] Options that will be passed to Keepassx::Entry#new.
|
67
|
+
# @return [Keepassx::Entry]
|
68
|
+
def add_entry(opts)
|
69
|
+
raise ArgumentError, "Expected Hash or Keepassx::Entry, got #{opts.class}" unless valid_entry?(opts)
|
30
70
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
71
|
+
if opts.is_a?(Keepassx::Entry)
|
72
|
+
# Add entry
|
73
|
+
@entries << opts
|
74
|
+
|
75
|
+
# Increment counter
|
76
|
+
header.entries_count += 1
|
77
|
+
|
78
|
+
# Return entry
|
79
|
+
opts
|
80
|
+
|
81
|
+
elsif opts.is_a?(Hash)
|
82
|
+
opts = deep_copy(opts)
|
83
|
+
opts = build_entry_options(opts)
|
84
|
+
|
85
|
+
# Create entry
|
86
|
+
entry = create_entry(opts)
|
87
|
+
|
88
|
+
# Increment counter
|
89
|
+
header.entries_count += 1
|
90
|
+
|
91
|
+
# Return entry
|
92
|
+
entry
|
93
|
+
end
|
35
94
|
end
|
36
95
|
|
37
|
-
|
38
|
-
|
96
|
+
|
97
|
+
def delete_group(item)
|
98
|
+
# Get group entries and delete them
|
99
|
+
group_entries = entries.select { |e| e.group == item }
|
100
|
+
group_entries.each { |e| delete_entry(e) }
|
101
|
+
|
102
|
+
# Recursively delete ancestor groups
|
103
|
+
group_ancestors = groups.select { |g| g.parent == item }
|
104
|
+
group_ancestors.each { |g| delete_group(g) }
|
105
|
+
|
106
|
+
item = groups.delete(item)
|
107
|
+
header.groups_count -= 1
|
108
|
+
item
|
39
109
|
end
|
40
110
|
|
41
|
-
|
42
|
-
|
111
|
+
|
112
|
+
def delete_entry(item)
|
113
|
+
item = entries.delete(item)
|
114
|
+
header.entries_count -= 1
|
115
|
+
item
|
43
116
|
end
|
117
|
+
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
|
122
|
+
# Make deep copy of Hash
|
123
|
+
def deep_copy(opts)
|
124
|
+
Marshal.load Marshal.dump(opts)
|
125
|
+
end
|
126
|
+
|
127
|
+
|
128
|
+
# Get next group ID number.
|
129
|
+
#
|
130
|
+
# @return [Fixnum]
|
131
|
+
def next_group_id
|
132
|
+
if @groups.empty?
|
133
|
+
# Start each time from 1 to make sure groups get the same id's for the
|
134
|
+
# same input data
|
135
|
+
1
|
136
|
+
else
|
137
|
+
id = @groups.last.id
|
138
|
+
loop do
|
139
|
+
id += 1
|
140
|
+
break id if @groups.find { |g| g.id == id }.nil?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# Retrieves last sibling index
|
147
|
+
#
|
148
|
+
# @param parent [Keepassx::Group] Last sibling group.
|
149
|
+
# @return [Integer] index Group index.
|
150
|
+
def last_sibling_index(parent)
|
151
|
+
return -1 if groups.empty?
|
152
|
+
|
153
|
+
if parent.nil?
|
154
|
+
parent_index = 0
|
155
|
+
sibling_level = 1
|
156
|
+
else
|
157
|
+
parent_index = groups.find_index(parent)
|
158
|
+
sibling_level = parent.level + 1
|
159
|
+
end
|
160
|
+
|
161
|
+
raise "Could not find group #{parent.name}" if parent_index.nil?
|
162
|
+
|
163
|
+
(parent_index..(header.groups_count - 1)).each do |i|
|
164
|
+
break i unless groups[i].level == sibling_level
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
def create_group(opts = {})
|
170
|
+
group = Keepassx::Group.new(opts)
|
171
|
+
if group.parent.nil?
|
172
|
+
@groups << group
|
173
|
+
else
|
174
|
+
index = last_sibling_index(group.parent) + 1
|
175
|
+
@groups.insert(index, group)
|
176
|
+
end
|
177
|
+
group
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def build_group_options(opts = {})
|
182
|
+
opts[:id] = next_group_id unless opts.key?(:id)
|
183
|
+
|
184
|
+
# Replace parent, which is specified by symbol with actual group
|
185
|
+
if opts[:parent].is_a?(Symbol)
|
186
|
+
group = find_group(opts[:parent])
|
187
|
+
raise "Group #{opts[:parent].inspect} does not exist" if group.nil?
|
188
|
+
|
189
|
+
opts[:parent] = group
|
190
|
+
end
|
191
|
+
opts
|
192
|
+
end
|
193
|
+
|
194
|
+
|
195
|
+
def create_entry(opts = {})
|
196
|
+
entry = Keepassx::Entry.new(opts)
|
197
|
+
@entries << entry
|
198
|
+
entry
|
199
|
+
end
|
200
|
+
|
201
|
+
|
202
|
+
# rubocop:disable Metrics/MethodLength, Style/SafeNavigation
|
203
|
+
def build_entry_options(opts = {})
|
204
|
+
if opts[:group]
|
205
|
+
if opts[:group].is_a?(String) || opts[:group].is_a?(Hash)
|
206
|
+
group = find_group(opts[:group])
|
207
|
+
raise "Group #{opts[:group].inspect} does not exist" if group.nil?
|
208
|
+
|
209
|
+
opts[:group] = group
|
210
|
+
opts[:group_id] = group.id
|
211
|
+
elsif opts[:group].is_a?(Keepassx::Group)
|
212
|
+
opts[:group_id] = opts[:group].id
|
213
|
+
end
|
214
|
+
|
215
|
+
elsif opts[:group_id] && opts[:group_id].is_a?(Integer)
|
216
|
+
group = find_group(id: opts[:group_id])
|
217
|
+
raise "Group #{opts[:group_id].inspect} does not exist" if group.nil?
|
218
|
+
|
219
|
+
opts[:group] = group
|
220
|
+
end
|
221
|
+
opts
|
222
|
+
end
|
223
|
+
# rubocop:enable Metrics/MethodLength, Style/SafeNavigation
|
224
|
+
|
225
|
+
|
226
|
+
def valid_group?(object)
|
227
|
+
object.is_a?(Keepassx::Group) || object.is_a?(Hash)
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
def valid_entry?(object)
|
232
|
+
object.is_a?(Keepassx::Entry) || object.is_a?(Hash)
|
233
|
+
end
|
234
|
+
|
44
235
|
end
|
45
236
|
end
|