passwordstate 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +33 -0
  3. data/.gitignore +2 -1
  4. data/.gitlab-ci.yml +10 -7
  5. data/.rubocop.yml +6 -9
  6. data/CHANGELOG.md +11 -0
  7. data/README.md +64 -16
  8. data/Rakefile +2 -2
  9. data/lib/passwordstate/client.rb +55 -16
  10. data/lib/passwordstate/errors.rb +6 -1
  11. data/lib/passwordstate/resource.rb +131 -49
  12. data/lib/passwordstate/resource_list.rb +56 -42
  13. data/lib/passwordstate/resources/active_directory.rb +23 -0
  14. data/lib/passwordstate/resources/address_book.rb +28 -0
  15. data/lib/passwordstate/resources/document.rb +32 -3
  16. data/lib/passwordstate/resources/folder.rb +6 -3
  17. data/lib/passwordstate/resources/host.rb +2 -0
  18. data/lib/passwordstate/resources/password.rb +50 -15
  19. data/lib/passwordstate/resources/password_list.rb +41 -8
  20. data/lib/passwordstate/resources/permission.rb +2 -0
  21. data/lib/passwordstate/resources/privileged_account.rb +32 -0
  22. data/lib/passwordstate/resources/remote_site.rb +26 -0
  23. data/lib/passwordstate/resources/report.rb +2 -0
  24. data/lib/passwordstate/util.rb +22 -2
  25. data/lib/passwordstate/version.rb +3 -1
  26. data/lib/passwordstate.rb +4 -1
  27. data/passwordstate.gemspec +9 -3
  28. data/test/client_test.rb +39 -0
  29. data/test/fixtures/get_password.json +31 -0
  30. data/test/fixtures/get_password_list.json +42 -0
  31. data/test/fixtures/get_password_otp.json +5 -0
  32. data/test/fixtures/password_list_search_passwords.json +60 -0
  33. data/test/fixtures/update_password.json +31 -0
  34. data/test/fixtures/update_password_managed.json +8 -0
  35. data/test/passwordstate_test.rb +10 -2
  36. data/test/resources/password_list_test.rb +81 -0
  37. data/test/resources/password_test.rb +105 -0
  38. data/test/test_helper.rb +8 -0
  39. metadata +56 -15
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordstate
2
4
  module Resources
3
5
  class Password < Passwordstate::Resource
@@ -5,6 +7,7 @@ module Passwordstate
5
7
 
6
8
  index_field :password_id
7
9
 
10
+ # rubocop:disable Naming/VariableNumber
8
11
  accessor_fields :title,
9
12
  :domain,
10
13
  :host_name,
@@ -20,6 +23,7 @@ module Passwordstate
20
23
  :generic_field_8, { name: 'GenericField8' },
21
24
  :generic_field_9, { name: 'GenericField9' },
22
25
  :generic_field_10, { name: 'GenericField10' },
26
+ :generic_field_info,
23
27
  :account_type_id, { name: 'AccountTypeID' },
24
28
  :notes,
25
29
  :url,
@@ -28,11 +32,17 @@ module Passwordstate
28
32
  :allow_export,
29
33
  :web_user_id, { name: 'WebUser_ID' },
30
34
  :web_password_id, { name: 'WebPassword_ID' },
31
- :password_list_id, { name: 'PasswordListID' } # Note: POST only # rubocop:disable Style/BracesAroundHashParameters
35
+ :password_list_id, { name: 'PasswordListID' } # NB: POST only
36
+ # rubocop:enable Naming/VariableNumber
32
37
 
33
38
  read_fields :account_type,
34
39
  :password_id, { name: 'PasswordID' },
35
- :password_list
40
+ :password_list,
41
+ :otp,
42
+ # For 'Managed' passwords
43
+ :status,
44
+ :current_password,
45
+ :new_password
36
46
 
37
47
  # Things that can be set in a POST/PUT request
38
48
  # TODO: Do this properly
@@ -47,18 +57,41 @@ module Passwordstate
47
57
  :heartbeat_enabled,
48
58
  :heartbeat_schedule,
49
59
  :validation_script_id, { name: 'ValidationScriptID' },
50
- :host_name,
51
60
  :ad_domain_netbios, { name: 'ADDomainNetBIOS' },
52
61
  :validate_with_priv_account
53
62
 
63
+ def otp!
64
+ client.request(:get, "onetimepassword/#{password_id}").first['OTP']
65
+ end
66
+
54
67
  def check_in
55
- client.request :get, "passwords/#{password_id}", query: passwordstatify_hash(check_in: nil)
68
+ client.request :get, "passwords/#{password_id}", query: self.class.passwordstateify_hash(check_in: nil)
69
+ end
70
+
71
+ def send_selfdestruct(email, expires_at:, view_count:, reason: nil, **params)
72
+ data = {
73
+ password_id: password_id,
74
+ to_email_address: email,
75
+ expires_at: expires_at,
76
+ no_views: view_count
77
+ }
78
+ data[:message] = params[:message] if params.key? :message
79
+ data[:prefix_message_content] = params[:prefix_message] if params.key? :prefix_message
80
+ data[:append_message_content] = params[:suffix_message] if params.key? :suffix_message
81
+ data[:to_first_name] = params[:name] if params.key? :name
82
+ data[:email_subject] = params[:subject] if params.key? :subject
83
+ data[:email_body] = params[:body] if params.key? :body
84
+ data[:passphrase] = params[:passphrase] if params.key? :passphrase
85
+ data[:reason] = reason if reason
86
+
87
+ client.request :post, 'selfdestruct', data: data
56
88
  end
57
89
 
58
90
  def history
59
91
  raise 'Password history only available on stored passwords' unless stored?
60
92
 
61
- Passwordstate::ResourceList.new client, PasswordHistory,
93
+ Passwordstate::ResourceList.new PasswordHistory,
94
+ client: client,
62
95
  all_path: "passwordhistory/#{password_id}",
63
96
  only: :all
64
97
  end
@@ -68,25 +101,25 @@ module Passwordstate
68
101
  PasswordPermission.new(_client: client, password_id: password_id)
69
102
  end
70
103
 
71
- def delete(recycle = false, query = {})
72
- super query.merge(move_to_recycle_bin: recycle)
104
+ def delete(recycle: false, **query)
105
+ super(**query.merge(move_to_recycle_bin: recycle))
73
106
  end
74
107
 
75
- def add_dependency(data = {})
108
+ def add_dependency(**data)
76
109
  raise 'Password dependency creation only available for stored passwords' unless stored?
77
110
 
78
111
  client.request :post, 'dependencies', body: self.class.passwordstatify_hash(data.merge(password_id: password_id))
79
112
  end
80
113
 
81
- def self.all(client, query = {})
82
- super client, { query_all: true }.merge(query)
114
+ def self.all(client, **query)
115
+ super client, **{ query_all: true }.merge(query)
83
116
  end
84
117
 
85
- def self.search(client, query = {})
86
- super client, { _api_path: 'searchpasswords' }.merge(query)
118
+ def self.search(client, **query)
119
+ super client, **{ _api_path: 'searchpasswords' }.merge(query)
87
120
  end
88
121
 
89
- def self.generate(client, options = {})
122
+ def self.generate(client, **options)
90
123
  results = client.request(:get, 'generatepassword', query: options).map { |r| r['Password'] }
91
124
  return results.first if results.count == 1
92
125
 
@@ -109,6 +142,7 @@ module Passwordstate
109
142
  :surname
110
143
 
111
144
  # Password object fields
145
+ # rubocop:disable Naming/VariableNumber
112
146
  read_fields :title,
113
147
  :domain,
114
148
  :host_name,
@@ -133,7 +167,8 @@ module Passwordstate
133
167
  :expiry_date, { is: Time },
134
168
  :allow_export,
135
169
  :web_user_id, { name: 'WebUser_ID' },
136
- :web_password_id, { name: 'WebPassword_ID' } # rubocop:disable Style/BracesAroundHashParameters
170
+ :web_password_id, { name: 'WebPassword_ID' }
171
+ # rubocop:enable Naming/VariableNumber
137
172
 
138
173
  def get
139
174
  raise 'Not applicable'
@@ -145,7 +180,7 @@ module Passwordstate
145
180
 
146
181
  index_field :password_id
147
182
 
148
- read_fields :password_id, { name: 'PasswordID' } # rubocop:disable Style/BracesAroundHashParameters
183
+ read_fields :password_id, { name: 'PasswordID' }
149
184
  end
150
185
  end
151
186
  end
@@ -1,7 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordstate
2
4
  module Resources
3
5
  class PasswordList < Passwordstate::Resource
6
+ HideConfig = Struct.new(:view, :modify, :admin) do
7
+ def self.parse(str)
8
+ view, modify, admin = str.split(':').map { |b| b.to_s.downcase == 'true' }
9
+
10
+ new view, modify, admin
11
+ end
12
+
13
+ def to_s
14
+ "#{view}:#{modify}:#{admin}"
15
+ end
16
+ end
17
+
4
18
  api_path 'passwordlists'
19
+ acceptable_methods :CR
5
20
 
6
21
  index_field :password_list_id
7
22
 
@@ -26,11 +41,13 @@ module Passwordstate
26
41
  :provide_access_reason,
27
42
  :password_reset_enabled,
28
43
  :force_password_generator,
29
- :hide_passwords,
44
+ :hide_passwords, { is: HideConfig },
30
45
  :show_guide,
31
46
  :enable_password_reset_schedule,
32
47
  :password_reset_schedule,
33
- :add_days_to_expiry_date
48
+ :add_to_expiry_date,
49
+ :add_to_expiry_date_interval,
50
+ :one_time_passwords
34
51
 
35
52
  read_fields :password_list_id, { name: 'PasswordListID' },
36
53
  :tree_path,
@@ -38,14 +55,27 @@ module Passwordstate
38
55
  :generator_name,
39
56
  :policy_name
40
57
 
58
+ write_fields :copy_settings_from_password_list_id, { name: 'CopySettinsgFromPasswordListID' },
59
+ :copy_settings_from_template_id, { name: 'CopySettingsFromTemplateID' },
60
+ :link_to_template,
61
+ :copy_permissions_from_password_list_id, { name: 'CopyPermissionsFromPasswordListID' },
62
+ :copy_permissions_from_template_id, { name: 'CopyPermissionsFromTemplateID' },
63
+ :nest_under_folder_id, { name: 'NestUnderFolderID' },
64
+ :apply_permissions_for_user_id, { name: 'ApplyPermissionsForUserID' },
65
+ :apply_permissions_for_security_group_id, { name: 'ApplyPermissionsForSecurityGroupID' },
66
+ :apply_permissions_for_security_group_name,
67
+ :permission,
68
+ :site_id, { name: 'SiteID' }
69
+
41
70
  alias title password_list
42
71
 
43
- def self.search(client, query = {})
44
- super client, query.merge(_api_path: 'searchpasswordlists')
72
+ def self.search(client, **query)
73
+ super client, **query.merge(_api_path: 'searchpasswordlists')
45
74
  end
46
75
 
47
76
  def passwords
48
- Passwordstate::ResourceList.new client, Passwordstate::Resources::Password,
77
+ Passwordstate::ResourceList.new Passwordstate::Resources::Password,
78
+ client: client,
49
79
  all_path: "passwords/#{password_list_id}",
50
80
  all_query: { query_all: nil },
51
81
  search_path: "searchpasswords/#{password_list_id}",
@@ -57,8 +87,11 @@ module Passwordstate
57
87
  PasswordListPermission.new(_client: client, password_list_id: password_list_id)
58
88
  end
59
89
 
60
- def full_path(unix = false)
61
- [tree_path, password_list].compact.join('\\').tap do |full|
90
+ def full_path(unix: false)
91
+ path = [tree_path]
92
+ path << password_list unless tree_path.end_with? password_list
93
+
94
+ path.compact.join('\\').tap do |full|
62
95
  full.tr!('\\', '/') if unix
63
96
  end
64
97
  end
@@ -69,7 +102,7 @@ module Passwordstate
69
102
 
70
103
  index_field :password_list_id
71
104
 
72
- read_fields :password_list_id, { name: 'PasswordListID' } # rubocop:disable Style/BracesAroundHashParameters
105
+ read_fields :password_list_id, { name: 'PasswordListID' }
73
106
  end
74
107
  end
75
108
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordstate
2
4
  module Resources
3
5
  class Permission < Passwordstate::Resource
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Passwordstate
4
+ module Resources
5
+ class PrivilegedAccount < Passwordstate::Resource
6
+ api_path 'privaccount'
7
+
8
+ index_field :privileged_account_id
9
+
10
+ accessor_fields :description,
11
+ :user_name,
12
+ :password,
13
+ :password_id, { name: 'PasswordID' },
14
+ :key_type,
15
+ :pass_phrase,
16
+ :private_key,
17
+ :site_id, { name: 'SiteID' },
18
+ :account_type,
19
+ :enable_password
20
+
21
+ read_fields :privileged_account_id
22
+ end
23
+
24
+ class PrivilegedAccountPermission < Permission
25
+ api_path 'privaccountpermissions'
26
+
27
+ index_field :privileged_account_id
28
+
29
+ read_fields :privileged_account_id, { name: 'PrivilegedAccountID' }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Passwordstate
4
+ module Resources
5
+ class RemoteSite < Passwordstate::Resource
6
+ api_path 'remotesitelocations'
7
+
8
+ index_field :site_id
9
+
10
+ accessor_fields :site_location,
11
+ :poll_frequency,
12
+ :maintenance_start,
13
+ :maintenance_end,
14
+ :gateway_url, { name: 'GatewayURL' },
15
+ :purge_session_recordings,
16
+ :discovery_threads,
17
+ :allowed_ip_ranges, { name: 'AllowedIPRanges' }
18
+
19
+ read_fields :site_id, { name: 'SiteID' }
20
+
21
+ def self.installer_instructions
22
+ client.request :get, "#{api_path}/exportinstallerinstructions"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordstate
2
4
  module Resources
3
5
  class Report < Passwordstate::Resource
@@ -1,7 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'net/http'
2
4
  require 'net/ntlm'
3
5
 
4
6
  module Net
7
+ # NTLM header extension
8
+ #
9
+ # @example Setting NTLM auth
10
+ # req = Net::HTTP::Get.new URI('https://example.com')
11
+ # req.ntlm_auth 'username', 'password'
5
12
  module HTTPHeader
6
13
  attr_reader :ntlm_auth_information, :ntlm_auth_options
7
14
 
@@ -20,10 +27,12 @@ module Net
20
27
  end
21
28
 
22
29
  class String
30
+ # Convert a snake_case string to CamelCase
23
31
  def camel_case
24
32
  split('_').collect(&:capitalize).join
25
33
  end
26
34
 
35
+ # Convert a CamelCase string to snake_case
27
36
  def snake_case
28
37
  gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
29
38
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
@@ -31,6 +40,7 @@ class String
31
40
  .downcase
32
41
  end
33
42
 
43
+ # Find a specific line in a block of text
34
44
  def find_line(&_block)
35
45
  raise ArgumentError, 'No block given' unless block_given?
36
46
 
@@ -41,6 +51,16 @@ class String
41
51
  end
42
52
 
43
53
  module Passwordstate
54
+ # Extensions on Net::HTTP to allow NTLM digest auth
55
+ #
56
+ # @example Using NTLM auth
57
+ # uri = URI('https://example.com/some_object')
58
+ # Net::HTTP.start uri.host, uri.port { |http|
59
+ # req = Net::HTTP::Get.new uri
60
+ # req.ntlm_auth 'username', 'password'
61
+ #
62
+ # http.request req
63
+ # }
44
64
  module NetHTTPExtensions
45
65
  def request(req, body = nil, &block)
46
66
  return super(req, body, &block) if req.ntlm_auth_information.nil?
@@ -55,7 +75,7 @@ module Passwordstate
55
75
  end
56
76
 
57
77
  type1 = Net::NTLM::Message::Type1.new
58
- req['authorization'] = 'NTLM ' + type1.encode64
78
+ req['authorization'] = "NTLM #{type1.encode64}"
59
79
  res = super(req, body)
60
80
 
61
81
  challenge = res['www-authenticate'][/(?:NTLM|Negotiate) (.+)/, 1]
@@ -64,7 +84,7 @@ module Passwordstate
64
84
  type2 = Net::NTLM::Message.decode64 challenge
65
85
  type3 = type2.response(req.ntlm_auth_information, req.ntlm_auth_options.dup)
66
86
 
67
- req['authorization'] = 'NTLM ' + type3.encode64
87
+ req['authorization'] = "NTLM #{type3.encode64}"
68
88
  req.body_stream.rewind if req.body_stream
69
89
  req.body = @last_body if @last_body
70
90
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Passwordstate
2
- VERSION = '0.0.3'.freeze
4
+ VERSION = '0.1.0'
3
5
  end
data/lib/passwordstate.rb CHANGED
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logging'
4
+ require 'pp'
5
+ require 'passwordstate/version'
2
6
  require 'passwordstate/client'
3
7
  require 'passwordstate/errors'
4
8
  require 'passwordstate/resource'
5
9
  require 'passwordstate/resource_list'
6
10
  require 'passwordstate/util'
7
- require 'passwordstate/version'
8
11
 
9
12
  module Passwordstate
10
13
  def self.debug!
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.join File.expand_path('lib', __dir__), 'passwordstate/version'
2
4
 
3
5
  Gem::Specification.new do |spec|
@@ -11,15 +13,19 @@ Gem::Specification.new do |spec|
11
13
  spec.homepage = 'https://github.com/ananace/ruby-passwordstate'
12
14
  spec.license = 'MIT'
13
15
 
16
+ spec.required_ruby_version = '>= 2.7'
17
+ spec.metadata['rubygems_mfa_required'] = 'true'
18
+
14
19
  spec.files = `git ls-files -z`.split("\x0")
15
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
20
  spec.require_paths = ['lib']
17
21
 
18
- spec.add_dependency 'logging', '~> 2.2'
19
- spec.add_dependency 'rubyntlm', '~> 0.6'
22
+ spec.add_dependency 'logging', '~> 2'
23
+ spec.add_dependency 'rubyntlm', '~> 0'
20
24
 
21
25
  spec.add_development_dependency 'bundler'
22
26
  spec.add_development_dependency 'minitest'
27
+ spec.add_development_dependency 'minitest-reporters'
28
+ spec.add_development_dependency 'mocha'
23
29
  spec.add_development_dependency 'rake'
24
30
  spec.add_development_dependency 'rubocop'
25
31
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ClientTest < Minitest::Test
6
+ def setup
7
+ Net::HTTP.any_instance.expects(:start).never
8
+
9
+ @client = Passwordstate::Client.new 'http://passwordstate.example.com'
10
+ end
11
+
12
+ def test_version
13
+ @client.expects(:request).with(:get, '', allow_html: true).returns <<~HTML
14
+ <html>
15
+ Lorem ipsum
16
+ <div><span>V</span>9.3 (Build 9200)</div>
17
+ </html>
18
+ HTML
19
+
20
+ assert_equal '9.3.9200', @client.version
21
+ assert @client.version? '~> 9.3'
22
+ end
23
+
24
+ # Passwordstate version 9.6 has started doing stupid things.
25
+ # There are no longer any available method to see the version.
26
+ def test_broken_version
27
+ @client.stubs(:request).with(:get, '', allow_html: true).returns <<~HTML
28
+
29
+
30
+
31
+ <script>
32
+ window.location.href = '/help/manuals/api/index.html';
33
+ </script>
34
+ HTML
35
+
36
+ assert_nil @client.version
37
+ assert @client.version? '~> 9.3'
38
+ end
39
+ end
@@ -0,0 +1,31 @@
1
+ [
2
+ {
3
+ "PasswordListID": "100",
4
+ "PasswordList": "Managed Passwords",
5
+ "PasswordID": 18075,
6
+ "Title": "borgdrone-4673615 @ Unimatrix Zero",
7
+ "Domain": "",
8
+ "HostName": "",
9
+ "UserName": "borgdrone-4673615",
10
+ "Description": "Unimatrix Zero access key for borgdrone-4673615",
11
+ "GenericField1": "",
12
+ "GenericField2": "",
13
+ "GenericField3": "",
14
+ "GenericField4": "",
15
+ "GenericField5": "",
16
+ "GenericField6": "",
17
+ "GenericField7": "",
18
+ "GenericField8": "",
19
+ "GenericField9": "",
20
+ "GenericField10": "",
21
+ "GenericFieldInfo": [],
22
+ "AccountTypeID": 0,
23
+ "Notes": "",
24
+ "URL": "",
25
+ "Password": "weishe4uChee0woh4ahquineibahpheiquaiy2yuRohjohChee6eet6rahbaiceetucoothooph8ooKahwoh5Teepah3ieLohwahkeigh4eefeivoohee8quee5oohoe",
26
+ "ExpiryDate": "",
27
+ "AllowExport": true,
28
+ "AccountType": "",
29
+ "OTP": null
30
+ }
31
+ ]
@@ -0,0 +1,42 @@
1
+ [
2
+ {
3
+ "PasswordListID": 100,
4
+ "PasswordList": "Managed Passwords",
5
+ "Description": "Someone's managed passwords",
6
+ "ImageFileName": "system.png",
7
+ "Guide": "",
8
+ "AllowExport": false,
9
+ "PrivatePasswordList": false,
10
+ "NoApprovers": "1",
11
+ "DisableNotifications": false,
12
+ "TimeBasedAccessRequired": false,
13
+ "PasswordStrengthPolicyID": 1,
14
+ "PasswordGeneratorID": 1,
15
+ "CodePage": "Using Passwordstate Default Code Page",
16
+ "PreventPasswordReuse": 0,
17
+ "AuthenticationType": "Use RADIUS Authentication",
18
+ "AuthenticationPerSession": false,
19
+ "PreventExpiryDateModification": true,
20
+ "SetExpiryDate": 0,
21
+ "ResetExpiryDate": 0,
22
+ "PreventDragDrop": true,
23
+ "PreventBadPasswordUse": false,
24
+ "ProvideAccessReason": true,
25
+ "TreePath": "\\Shared\\Somewhere\\Managed Passwords",
26
+ "TotalPasswords": 656,
27
+ "GeneratorName": "Corporate Password Generator",
28
+ "PolicyName": "Corporate Password Strength Policy",
29
+ "PasswordResetEnabled": false,
30
+ "ForcePasswordGenerator": false,
31
+ "HidePasswords": "False:True:False",
32
+ "ShowGuide": false,
33
+ "EnablePasswordResetSchedule": false,
34
+ "PasswordResetSchedule": "00:00:00:00",
35
+ "AddToExpiryDate": 90,
36
+ "AddToExpiryDateInterval": "Days",
37
+ "SiteID": 0,
38
+ "SiteLocation": "Internal",
39
+ "OneTimePasswords": false,
40
+ "DisableInheritance": true
41
+ }
42
+ ]
@@ -0,0 +1,5 @@
1
+ [
2
+ {
3
+ "OTP": "123456"
4
+ }
5
+ ]
@@ -0,0 +1,60 @@
1
+ [
2
+ {
3
+ "PasswordListID": "100",
4
+ "PasswordList": "Managed Passwords",
5
+ "PasswordID": 18075,
6
+ "Title": "borgdrone-4673615 @ Unimatrix Zero",
7
+ "Domain": "",
8
+ "HostName": "",
9
+ "UserName": "borgdrone-4673615",
10
+ "Description": "Unimatrix Zero access key for borgdrone-4673615",
11
+ "GenericField1": "",
12
+ "GenericField2": "",
13
+ "GenericField3": "",
14
+ "GenericField4": "",
15
+ "GenericField5": "",
16
+ "GenericField6": "",
17
+ "GenericField7": "",
18
+ "GenericField8": "",
19
+ "GenericField9": "",
20
+ "GenericField10": "",
21
+ "GenericFieldInfo": [],
22
+ "AccountTypeID": 0,
23
+ "Notes": "",
24
+ "URL": "",
25
+ "Password": "weishe4uChee0woh4ahquineibahpheiquaiy2yuRohjohChee6eet6rahbaiceetucoothooph8ooKahwoh5Teepah3ieLohwahkeigh4eefeivoohee8quee5oohoe",
26
+ "ExpiryDate": "",
27
+ "AllowExport": true,
28
+ "AccountType": "",
29
+ "OTP": null
30
+ },
31
+ {
32
+ "PasswordListID": "100",
33
+ "PasswordList": "Managed Passwords",
34
+ "PasswordID": 29467,
35
+ "Title": "borgdrone-9342756 @ Unimatrix Zero",
36
+ "Domain": "",
37
+ "HostName": "",
38
+ "UserName": "borgdrone-9342756",
39
+ "Description": "Unimatrix Zero access key for borgdrone-9342756",
40
+ "GenericField1": "",
41
+ "GenericField2": "",
42
+ "GenericField3": "",
43
+ "GenericField4": "",
44
+ "GenericField5": "",
45
+ "GenericField6": "",
46
+ "GenericField7": "",
47
+ "GenericField8": "",
48
+ "GenericField9": "",
49
+ "GenericField10": "",
50
+ "GenericFieldInfo": [],
51
+ "AccountTypeID": 0,
52
+ "Notes": "",
53
+ "URL": "",
54
+ "Password": "chaihohd6aemeitivaefei0zahjee0IN7aevaisoGohrae5ileeHaquaik7lo1aicoo3lohchae2nujohRu2oofiizieth6aezahng8ohp9siesaemahKaifah8Ooyoo",
55
+ "ExpiryDate": "",
56
+ "AllowExport": true,
57
+ "AccountType": "",
58
+ "OTP": null
59
+ }
60
+ ]
@@ -0,0 +1,31 @@
1
+ [
2
+ {
3
+ "PasswordListID": "100",
4
+ "PasswordList": "Managed Passwords",
5
+ "PasswordID": 18075,
6
+ "Title": "borgdrone-4673615 @ Unimatrix Zero",
7
+ "Domain": "1c0389a1-6e63-4276-8500-1e595f0288e9.cube.collective",
8
+ "HostName": "",
9
+ "UserName": "borgdrone-4673615",
10
+ "Description": "Unimatrix Zero access key for borgdrone-4673615",
11
+ "GenericField1": "",
12
+ "GenericField2": "",
13
+ "GenericField3": "",
14
+ "GenericField4": "",
15
+ "GenericField5": "",
16
+ "GenericField6": "",
17
+ "GenericField7": "",
18
+ "GenericField8": "",
19
+ "GenericField9": "",
20
+ "GenericField10": "",
21
+ "GenericFieldInfo": [],
22
+ "AccountTypeID": 0,
23
+ "Notes": "",
24
+ "URL": "",
25
+ "Password": "weishe4uChee0woh4ahquineibahpheiquaiy2yuRohjohChee6eet6rahbaiceetucoothooph8ooKahwoh5Teepah3ieLohwahkeigh4eefeivoohee8quee5oohoe",
26
+ "ExpiryDate": "",
27
+ "AllowExport": true,
28
+ "AccountType": "",
29
+ "OTP": null
30
+ }
31
+ ]
@@ -0,0 +1,8 @@
1
+ [
2
+ {
3
+ "PasswordID": 18075,
4
+ "Status": "Password Queued for Reset(s). Check auditing data, or UI for results.",
5
+ "CurrentPassword": "weishe4uChee0woh4ahquineibahpheiquaiy2yuRohjohChee6eet6rahbaiceetucoothooph8ooKahwoh5Teepah3ieLohwahkeigh4eefeivoohee8quee5oohoe",
6
+ "NewPassword": "<redacted>"
7
+ }
8
+ ]