keepassx 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![GitHub license](https://img.shields.io/github/license/pitluga/keepassx.svg)](https://github.com/pitluga/keepassx/blob/master/LICENSE)
|
4
|
+
[![GitHub release](https://img.shields.io/github/release/pitluga/keepassx.svg)](https://github.com/pitluga/keepassx/releases/latest)
|
5
|
+
[![Build Status](https://travis-ci.org/pitluga/keepassx.svg?branch=master)](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
|