passwordstate 0.0.4 → 0.1.1
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 +4 -4
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +2 -1
- data/.gitlab-ci.yml +10 -7
- data/.rubocop.yml +6 -9
- data/CHANGELOG.md +7 -0
- data/README.md +64 -16
- data/Rakefile +2 -2
- data/lib/passwordstate/client.rb +62 -16
- data/lib/passwordstate/errors.rb +6 -1
- data/lib/passwordstate/resource.rb +137 -53
- data/lib/passwordstate/resource_list.rb +56 -42
- data/lib/passwordstate/resources/active_directory.rb +23 -0
- data/lib/passwordstate/resources/address_book.rb +28 -0
- data/lib/passwordstate/resources/document.rb +32 -3
- data/lib/passwordstate/resources/folder.rb +6 -3
- data/lib/passwordstate/resources/host.rb +2 -0
- data/lib/passwordstate/resources/password.rb +50 -15
- data/lib/passwordstate/resources/password_list.rb +41 -8
- data/lib/passwordstate/resources/permission.rb +2 -0
- data/lib/passwordstate/resources/privileged_account.rb +32 -0
- data/lib/passwordstate/resources/remote_site.rb +26 -0
- data/lib/passwordstate/resources/report.rb +2 -0
- data/lib/passwordstate/util.rb +22 -2
- data/lib/passwordstate/version.rb +3 -1
- data/lib/passwordstate.rb +4 -1
- data/passwordstate.gemspec +9 -3
- data/test/client_test.rb +39 -0
- data/test/fixtures/get_password.json +31 -0
- data/test/fixtures/get_password_list.json +42 -0
- data/test/fixtures/get_password_otp.json +5 -0
- data/test/fixtures/password_list_search_passwords.json +60 -0
- data/test/fixtures/update_password.json +31 -0
- data/test/fixtures/update_password_managed.json +8 -0
- data/test/passwordstate_test.rb +10 -2
- data/test/resources/password_list_test.rb +81 -0
- data/test/resources/password_test.rb +105 -0
- data/test/test_helper.rb +8 -0
- metadata +56 -15
@@ -1,57 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Passwordstate
|
2
|
-
# A simple resource DSL
|
4
|
+
# A simple resource DSL helper
|
5
|
+
#
|
6
|
+
# rubocop:disable Metrics/ClassLength This DSL class will be large
|
3
7
|
class Resource
|
4
8
|
attr_reader :client
|
5
9
|
|
6
|
-
|
7
|
-
|
10
|
+
# Update the object based off of up-to-date upstream data
|
11
|
+
# @return [Resource] self
|
12
|
+
def get(**query)
|
13
|
+
set! self.class.get(client, send(self.class.index_field), **query)
|
8
14
|
end
|
9
15
|
|
10
|
-
|
16
|
+
# Push any unapplied changes to the object
|
17
|
+
# @return [Resource] self
|
18
|
+
def put(body = {}, **query)
|
11
19
|
to_send = modified.merge(self.class.index_field => send(self.class.index_field))
|
12
|
-
set! self.class.put(client, to_send.merge(body), query).first
|
20
|
+
set! self.class.put(client, to_send.merge(body), **query).first
|
13
21
|
end
|
14
22
|
|
15
|
-
|
16
|
-
|
23
|
+
# Create the object based off of provided information
|
24
|
+
# @return [Resource] self
|
25
|
+
def post(body = {}, **query)
|
26
|
+
set! self.class.post(client, attributes.merge(body), **query)
|
17
27
|
end
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
# Delete the object
|
30
|
+
# @return [Resource] self
|
31
|
+
def delete(**query)
|
32
|
+
self.class.delete(client, send(self.class.index_field), **query)
|
21
33
|
end
|
22
34
|
|
23
35
|
def initialize(data)
|
24
36
|
@client = data.delete :_client
|
25
|
-
set! data, false
|
37
|
+
set! data, store_old: false
|
26
38
|
old
|
27
39
|
end
|
28
40
|
|
41
|
+
# Is the object stored on the Passwordstate server
|
29
42
|
def stored?
|
30
43
|
!send(self.class.index_field).nil?
|
31
44
|
end
|
32
45
|
|
46
|
+
# Check if the resource type is available on the connected Passwordstate server
|
33
47
|
def self.available?(_client)
|
34
48
|
true
|
35
49
|
end
|
36
50
|
|
37
|
-
|
51
|
+
# Retrieve all accessible instances of the resource type, requires the method :get
|
52
|
+
def self.all(client, **query)
|
53
|
+
raise NotAcceptableError, "Read is not implemented for #{self}" unless acceptable_methods.include? :get
|
54
|
+
|
38
55
|
path = query.fetch(:_api_path, api_path)
|
56
|
+
reason = query.delete(:_reason)
|
39
57
|
query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
|
40
58
|
|
41
|
-
[client.request(:get, path, query: query)].flatten.map do |object|
|
59
|
+
[client.request(:get, path, query: query, reason: reason)].flatten.map do |object|
|
42
60
|
new object.merge(_client: client)
|
43
61
|
end
|
44
62
|
end
|
45
63
|
|
46
|
-
|
64
|
+
# Retrieve a specific instance of the resource type, requires the method :get
|
65
|
+
def self.get(client, object, **query)
|
66
|
+
raise NotAcceptableError, "Read is not implemented for #{self}" unless acceptable_methods.include? :get
|
67
|
+
|
47
68
|
object = object.send(object.class.send(index_field)) if object.is_a? Resource
|
48
69
|
|
49
70
|
return new _client: client, index_field => object if query[:_bare]
|
50
71
|
|
51
72
|
path = query.fetch(:_api_path, api_path)
|
73
|
+
reason = query.delete(:_reason)
|
52
74
|
query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
|
53
75
|
|
54
|
-
resp = client.request(:get, "#{path}/#{object}", query: query).map do |data|
|
76
|
+
resp = client.request(:get, "#{path}/#{object}", query: query, reason: reason).map do |data|
|
55
77
|
new data.merge(_client: client)
|
56
78
|
end
|
57
79
|
return resp.first if resp.one? || resp.empty?
|
@@ -59,70 +81,98 @@ module Passwordstate
|
|
59
81
|
resp
|
60
82
|
end
|
61
83
|
|
62
|
-
|
84
|
+
# Create a new instance of the resource type, requires the method :post
|
85
|
+
def self.post(client, data, **query)
|
86
|
+
raise NotAcceptableError, "Create is not implemented for #{self}" unless acceptable_methods.include? :post
|
87
|
+
|
63
88
|
path = query.fetch(:_api_path, api_path)
|
89
|
+
reason = query.delete(:_reason)
|
64
90
|
data = passwordstateify_hash data
|
65
91
|
query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
|
66
92
|
|
67
|
-
new [client.request(:post, path, body: data, query: query)].flatten.first.merge(_client: client)
|
93
|
+
new [client.request(:post, path, body: data, query: query, reason: reason)].flatten.first.merge(_client: client)
|
68
94
|
end
|
69
95
|
|
70
|
-
|
96
|
+
# Push new data to an instance of the resource type, requires the method :put
|
97
|
+
def self.put(client, data, **query)
|
98
|
+
raise NotAcceptableError, "Update is not implemented for #{self}" unless acceptable_methods.include? :put
|
99
|
+
|
71
100
|
path = query.fetch(:_api_path, api_path)
|
101
|
+
reason = query.delete(:_reason)
|
72
102
|
data = passwordstateify_hash data
|
73
103
|
query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
|
74
104
|
|
75
|
-
client.request :put, path, body: data, query: query
|
105
|
+
client.request :put, path, body: data, query: query, reason: reason
|
76
106
|
end
|
77
107
|
|
78
|
-
|
108
|
+
# Delete an instance of the resource type, requires the method :delete
|
109
|
+
def self.delete(client, object, **query)
|
110
|
+
raise NotAcceptableError, "Delete is not implemented for #{self}" unless acceptable_methods.include? :delete
|
111
|
+
|
79
112
|
path = query.fetch(:_api_path, api_path)
|
113
|
+
reason = query.delete(:_reason)
|
80
114
|
query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
|
81
115
|
|
82
116
|
object = object.send(object.class.send(index_field)) if object.is_a? Resource
|
83
|
-
client.request :delete, "#{path}/#{object}", query: query
|
84
|
-
end
|
85
|
-
|
86
|
-
def self.passwordstateify_hash(hash)
|
87
|
-
Hash[hash.map { |k, v| [ruby_to_passwordstate_field(k), v] }]
|
117
|
+
client.request :delete, "#{path}/#{object}", query: query, reason: reason
|
88
118
|
end
|
89
119
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
120
|
+
# Get a hash of all active attributes on the resource instance
|
121
|
+
#
|
122
|
+
# @param ignore_redact [Boolean] Should any normally redacted fields be displayed in clear-text
|
123
|
+
# @param atify [Boolean] Should the resulting hash be returned with instance variable names ('@'-prefixed)
|
124
|
+
# @option opts nil_as_string [Boolean] (resource dependent) Should nil values be treated as the empty string
|
125
|
+
def attributes(ignore_redact: true, atify: false, **opts)
|
96
126
|
nil_as_string = opts.fetch(:nil_as_string, self.class.nil_as_string)
|
97
|
-
|
127
|
+
(self.class.send(:accessor_field_names) + self.class.send(:read_field_names) + self.class.send(:write_field_names)).to_h do |field|
|
98
128
|
redact = self.class.send(:field_options)[field]&.fetch(:redact, false) && !ignore_redact
|
99
|
-
|
129
|
+
at_field = "@#{field}".to_sym
|
130
|
+
field = at_field if atify
|
131
|
+
value = instance_variable_get(at_field) unless redact
|
100
132
|
value = '[ REDACTED ]' if redact
|
101
133
|
value = '' if value.nil? && nil_as_string
|
102
134
|
[field, value]
|
103
|
-
end
|
135
|
+
end.compact
|
104
136
|
end
|
105
137
|
|
106
|
-
def
|
107
|
-
|
138
|
+
def pretty_print(pp)
|
139
|
+
pp.object_address_group(self) do
|
140
|
+
attrs = attributes(atify: true, nil_as_string: false, ignore_redact: false).compact
|
141
|
+
pp.seplist(attrs.keys.sort, -> { pp.text ',' }) do |v|
|
142
|
+
pp.breakable
|
143
|
+
v = v.to_s if v.is_a? Symbol
|
144
|
+
pp.text v
|
145
|
+
pp.text '='
|
146
|
+
pp.group(1) do
|
147
|
+
pp.breakable ''
|
148
|
+
pp.pp(attrs[v.to_sym])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def modified?(field = nil)
|
155
|
+
return modified.any? unless field
|
156
|
+
|
157
|
+
modified.include? field
|
108
158
|
end
|
109
159
|
|
110
160
|
protected
|
111
161
|
|
162
|
+
def api_path
|
163
|
+
self.class.instance_variable_get :@api_path
|
164
|
+
end
|
165
|
+
|
112
166
|
def modified
|
113
167
|
attribs = attributes
|
114
168
|
attribs.reject { |field| old[field] == attribs[field] }
|
115
169
|
end
|
116
170
|
|
117
|
-
def modified?(field)
|
118
|
-
modified.include? field
|
119
|
-
end
|
120
|
-
|
121
171
|
def old
|
122
172
|
@old ||= attributes.dup
|
123
173
|
end
|
124
174
|
|
125
|
-
def set!(data, store_old
|
175
|
+
def set!(data, store_old: true)
|
126
176
|
@old = attributes.dup if store_old
|
127
177
|
data = data.attributes if data.is_a? Passwordstate::Resource
|
128
178
|
data.each do |key, value|
|
@@ -133,8 +183,11 @@ module Passwordstate
|
|
133
183
|
|
134
184
|
if !value.nil? && opts&.key?(:is)
|
135
185
|
klass = opts.fetch(:is)
|
136
|
-
parsed_value = klass.send :parse, value rescue nil if klass.respond_to?
|
137
|
-
parsed_value ||= klass.send :new, value rescue nil if klass.respond_to? :
|
186
|
+
parsed_value = klass.send :parse, value rescue nil if klass.respond_to?(:parse)
|
187
|
+
parsed_value ||= klass.send :new, value rescue nil if klass.respond_to?(:new) && !klass.respond_to?(:parse)
|
188
|
+
elsif !value.nil? && opts&.key?(:convert)
|
189
|
+
convert = opts.fetch(:convert)
|
190
|
+
parsed_value = convert.call(value, direction: :from)
|
138
191
|
end
|
139
192
|
|
140
193
|
instance_variable_set "@#{field}".to_sym, parsed_value || value
|
@@ -145,26 +198,52 @@ module Passwordstate
|
|
145
198
|
class << self
|
146
199
|
alias search all
|
147
200
|
|
201
|
+
# Get/Set the API path for the resource type
|
148
202
|
def api_path(path = nil)
|
149
203
|
@api_path = path unless path.nil?
|
150
204
|
@api_path
|
151
205
|
end
|
152
206
|
|
207
|
+
# Get/Set the index field for the resource type
|
153
208
|
def index_field(field = nil)
|
154
209
|
@index_field = field unless field.nil?
|
155
210
|
@index_field
|
156
211
|
end
|
157
212
|
|
213
|
+
# Get/Set whether the resource type requires nil values to be handled as the empty string
|
158
214
|
def nil_as_string(opt = nil)
|
159
215
|
@nil_as_string = opt unless opt.nil?
|
160
216
|
@nil_as_string
|
161
217
|
end
|
162
218
|
|
219
|
+
# Get/Set acceptable CRUD methods for the resource type
|
220
|
+
def acceptable_methods(*meths)
|
221
|
+
if meths.empty?
|
222
|
+
@acceptable_methods || %i[post get put delete]
|
223
|
+
elsif meths.count == 1 && meths.to_s.upcase == meths.to_s
|
224
|
+
@acceptable_methods = []
|
225
|
+
meths = meths.first.to_s
|
226
|
+
@acceptable_methods << :post if meths.include? 'C'
|
227
|
+
@acceptable_methods << :get if meths.include? 'R'
|
228
|
+
@acceptable_methods << :put if meths.include? 'U'
|
229
|
+
@acceptable_methods << :delete if meths.include? 'D'
|
230
|
+
else
|
231
|
+
@acceptable_methods = meths
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
# Convert a hash from Ruby syntax to Passwordstate syntax
|
236
|
+
def passwordstateify_hash(hash)
|
237
|
+
hash.transform_keys { |k| ruby_to_passwordstate_field(k) }
|
238
|
+
end
|
239
|
+
|
240
|
+
# Convert a Passwordstate field name into Ruby syntax
|
163
241
|
def passwordstate_to_ruby_field(field)
|
164
242
|
opts = send(:field_options).find { |(_k, v)| v[:name] == field }
|
165
243
|
opts&.first || field.to_s.snake_case.to_sym
|
166
244
|
end
|
167
245
|
|
246
|
+
# Convert a Ruby field name into Passwordstate syntax
|
168
247
|
def ruby_to_passwordstate_field(field)
|
169
248
|
send(:field_options)[field]&.[](:name) || field.to_s.camel_case
|
170
249
|
end
|
@@ -225,18 +304,23 @@ module Passwordstate
|
|
225
304
|
end
|
226
305
|
end
|
227
306
|
end
|
307
|
+
# rubocop:enable Metrics/ClassLength
|
228
308
|
|
229
309
|
module Resources
|
230
|
-
autoload :
|
231
|
-
autoload :
|
232
|
-
autoload :
|
233
|
-
autoload :
|
234
|
-
autoload :
|
235
|
-
autoload :
|
236
|
-
autoload :Password,
|
237
|
-
autoload :
|
238
|
-
autoload :
|
239
|
-
autoload :
|
240
|
-
autoload :
|
310
|
+
autoload :ActiveDirectory, 'passwordstate/resources/active_directory'
|
311
|
+
autoload :AddressBook, 'passwordstate/resources/address_book'
|
312
|
+
autoload :Document, 'passwordstate/resources/document'
|
313
|
+
autoload :Folder, 'passwordstate/resources/folder'
|
314
|
+
autoload :FolderPermission, 'passwordstate/resources/folder'
|
315
|
+
autoload :Host, 'passwordstate/resources/host'
|
316
|
+
autoload :Password, 'passwordstate/resources/password'
|
317
|
+
autoload :PasswordList, 'passwordstate/resources/password_list'
|
318
|
+
autoload :PasswordListPermission, 'passwordstate/resources/password_list'
|
319
|
+
autoload :PasswordHistory, 'passwordstate/resources/password'
|
320
|
+
autoload :PasswordPermission, 'passwordstate/resources/password_list'
|
321
|
+
autoload :PrivilegedAccount, 'passwordstate/resources/privileged_account'
|
322
|
+
autoload :PrivilegedAccountPermission, 'passwordstate/resources/privileged_account'
|
323
|
+
autoload :Permission, 'passwordstate/resources/permission'
|
324
|
+
autoload :Report, 'passwordstate/resources/report'
|
241
325
|
end
|
242
326
|
end
|
@@ -1,45 +1,49 @@
|
|
1
|
-
|
2
|
-
class ResourceList < Array
|
3
|
-
Array.public_instance_methods(false).each do |method|
|
4
|
-
next if %i[reject select slice clear inspect].include?(method.to_sym)
|
5
|
-
|
6
|
-
class_eval <<-EVAL, __FILE__, __LINE__ + 1
|
7
|
-
def #{method}(*args)
|
8
|
-
lazy_load unless @loaded
|
9
|
-
super
|
10
|
-
end
|
11
|
-
EVAL
|
12
|
-
end
|
1
|
+
# frozen_string_literal: true
|
13
2
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
lazy_load unless @loaded
|
18
|
-
data = super
|
19
|
-
self.clone.clear.concat(data)
|
20
|
-
end
|
21
|
-
EVAL
|
22
|
-
end
|
23
|
-
|
24
|
-
def inspect
|
25
|
-
lazy_load unless @loaded
|
26
|
-
super
|
27
|
-
end
|
3
|
+
module Passwordstate
|
4
|
+
class ResourceList
|
5
|
+
include Enumerable
|
28
6
|
|
29
7
|
attr_reader :client, :resource, :options
|
30
8
|
|
31
|
-
def initialize(
|
9
|
+
def initialize(resource, client:, **options)
|
32
10
|
@client = client
|
33
11
|
@resource = resource
|
34
12
|
@loaded = false
|
35
13
|
@options = options
|
14
|
+
@data = []
|
36
15
|
|
37
16
|
options[:only] = [options[:only]].flatten if options.key? :only
|
38
17
|
options[:except] = [options[:except]].flatten if options.key? :except
|
39
18
|
end
|
40
19
|
|
20
|
+
def pretty_print_instance_variables
|
21
|
+
instance_variables.reject { |k| %i[@client @data].include? k }.sort
|
22
|
+
end
|
23
|
+
|
24
|
+
def pretty_print(pp)
|
25
|
+
return pp.pp self if respond_to? :mocha_inspect
|
26
|
+
|
27
|
+
pp.pp_object(self)
|
28
|
+
end
|
29
|
+
|
30
|
+
alias inspect pretty_print_inspect
|
31
|
+
|
32
|
+
def each(&block)
|
33
|
+
lazy_load unless @loaded
|
34
|
+
|
35
|
+
return to_enum(__method__) { @data.size } unless block_given?
|
36
|
+
|
37
|
+
@data.each(&block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](index)
|
41
|
+
@data[index]
|
42
|
+
end
|
43
|
+
|
41
44
|
def clear
|
42
|
-
@
|
45
|
+
@data = []
|
46
|
+
@loaded = false
|
43
47
|
end
|
44
48
|
|
45
49
|
def reload
|
@@ -48,9 +52,12 @@ module Passwordstate
|
|
48
52
|
end
|
49
53
|
|
50
54
|
def load(entries)
|
51
|
-
clear
|
55
|
+
clear
|
56
|
+
entries.tap do |loaded|
|
52
57
|
loaded.sort! { |a, b| a.send(a.class.index_field) <=> b.send(b.class.index_field) } if options.fetch(:sort, true)
|
53
|
-
end
|
58
|
+
end
|
59
|
+
entries.each { |obj| @data << obj }
|
60
|
+
@loaded = true
|
54
61
|
self
|
55
62
|
end
|
56
63
|
|
@@ -74,58 +81,65 @@ module Passwordstate
|
|
74
81
|
obj
|
75
82
|
end
|
76
83
|
|
77
|
-
def search(query
|
84
|
+
def search(**query)
|
78
85
|
raise 'Operation not supported' unless operation_supported?(:search)
|
79
86
|
|
80
87
|
api_path = options.fetch(:search_path, resource.api_path)
|
81
88
|
query = options.fetch(:search_query, {}).merge(query)
|
82
89
|
|
83
|
-
resource.search(client, query.merge(_api_path: api_path))
|
90
|
+
resource.search(client, **query.merge(_api_path: api_path))
|
84
91
|
end
|
85
92
|
|
86
|
-
def all(query
|
93
|
+
def all(**query)
|
87
94
|
raise 'Operation not supported' unless operation_supported?(:all)
|
88
95
|
|
89
96
|
api_path = options.fetch(:all_path, resource.api_path)
|
90
97
|
query = options.fetch(:all_query, {}).merge(query)
|
91
98
|
|
92
|
-
load resource.all(client, query.merge(_api_path: api_path))
|
99
|
+
load resource.all(client, **query.merge(_api_path: api_path))
|
93
100
|
end
|
94
101
|
|
95
|
-
def get(id, query
|
102
|
+
def get(id, **query)
|
96
103
|
raise 'Operation not supported' unless operation_supported?(:get)
|
97
104
|
|
105
|
+
if query.empty? && !@data.empty?
|
106
|
+
existing = @data.find do |entry|
|
107
|
+
entry.send(entry.class.index_field) == id
|
108
|
+
end
|
109
|
+
return existing if existing
|
110
|
+
end
|
111
|
+
|
98
112
|
api_path = options.fetch(:get_path, resource.api_path)
|
99
113
|
query = options.fetch(:get_query, {}).merge(query)
|
100
114
|
|
101
|
-
resource.get(client, id, query.merge(_api_path: api_path))
|
115
|
+
resource.get(client, id, **query.merge(_api_path: api_path))
|
102
116
|
end
|
103
117
|
|
104
|
-
def post(data, query
|
118
|
+
def post(data, **query)
|
105
119
|
raise 'Operation not supported' unless operation_supported?(:post)
|
106
120
|
|
107
121
|
api_path = options.fetch(:post_path, resource.api_path)
|
108
122
|
query = options.fetch(:post_query, {}).merge(query)
|
109
123
|
|
110
|
-
resource.post(client, data, query.merge(_api_path: api_path))
|
124
|
+
resource.post(client, data, **query.merge(_api_path: api_path))
|
111
125
|
end
|
112
126
|
|
113
|
-
def put(data, query
|
127
|
+
def put(data, **query)
|
114
128
|
raise 'Operation not supported' unless operation_supported?(:put)
|
115
129
|
|
116
130
|
api_path = options.fetch(:put_path, resource.api_path)
|
117
131
|
query = options.fetch(:put_query, {}).merge(query)
|
118
132
|
|
119
|
-
resource.put(client, data, query.merge(_api_path: api_path))
|
133
|
+
resource.put(client, data, **query.merge(_api_path: api_path))
|
120
134
|
end
|
121
135
|
|
122
|
-
def delete(id, query
|
136
|
+
def delete(id, **query)
|
123
137
|
raise 'Operation not supported' unless operation_supported?(:delete)
|
124
138
|
|
125
139
|
api_path = options.fetch(:delete_path, resource.api_path)
|
126
140
|
query = options.fetch(:delete_query, {}).merge(query)
|
127
141
|
|
128
|
-
resource.delete(client, id, query.merge(_api_path: api_path))
|
142
|
+
resource.delete(client, id, **query.merge(_api_path: api_path))
|
129
143
|
end
|
130
144
|
|
131
145
|
private
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Passwordstate
|
4
|
+
module Resources
|
5
|
+
class ActiveDirectory < Passwordstate::Resource
|
6
|
+
api_path 'activedirectory'
|
7
|
+
|
8
|
+
index_field :ad_domain_id
|
9
|
+
|
10
|
+
accessor_fields :ad_domain_netbios, { name: 'ADDomainNetBIOS' },
|
11
|
+
:ad_domain_ldap, { name: 'ADDomainLDAP' },
|
12
|
+
:fqdn, { name: 'FQDN' },
|
13
|
+
:default_domain,
|
14
|
+
:pa_read_id, { name: 'PAReadID' },
|
15
|
+
:site_id, { name: 'SiteID' },
|
16
|
+
:used_for_authentication,
|
17
|
+
:protocol,
|
18
|
+
:domain_controller_fqdn, { name: 'DomainControllerFQDN' }
|
19
|
+
|
20
|
+
read_fields :ad_domain_id, { name: 'ADDomainID' }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Passwordstate
|
4
|
+
module Resources
|
5
|
+
class AddressBook < Passwordstate::Resource
|
6
|
+
api_path 'addressbook'
|
7
|
+
|
8
|
+
index_field :address_book_id
|
9
|
+
|
10
|
+
accessor_fields :first_name,
|
11
|
+
:surname,
|
12
|
+
:email_adress,
|
13
|
+
:company,
|
14
|
+
:business_phone,
|
15
|
+
:personal_phone,
|
16
|
+
:street,
|
17
|
+
:city,
|
18
|
+
:state,
|
19
|
+
:zipcode,
|
20
|
+
:country,
|
21
|
+
:notes,
|
22
|
+
:pass_phrase,
|
23
|
+
:global_contact
|
24
|
+
|
25
|
+
read_fields :address_book_id, { name: 'AddressBookID' }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Passwordstate
|
2
4
|
module Resources
|
3
5
|
class Document < Passwordstate::Resource
|
@@ -10,12 +12,39 @@ module Passwordstate
|
|
10
12
|
|
11
13
|
alias title document_name
|
12
14
|
|
13
|
-
def self.
|
14
|
-
client.
|
15
|
+
def self.all(client, store, **query)
|
16
|
+
super client, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.search(client, store, **options)
|
20
|
+
all client, store, **options
|
15
21
|
end
|
16
22
|
|
17
23
|
def self.get(client, store, object)
|
18
|
-
client
|
24
|
+
super client, object, _api_path: "#{api_path}/#{validate_store! store}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.post(client, store, data, **query)
|
28
|
+
super client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.put(client, store, data, **query)
|
32
|
+
super client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.delete(client, store, object, **query)
|
36
|
+
super client, object, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
private
|
41
|
+
|
42
|
+
def validate_store!(store)
|
43
|
+
raise ArgumentError, 'Store must be one of password, passwordlist, folder' \
|
44
|
+
unless %i[password passwordlist folder].include?(store.to_s.downcase.to_sym)
|
45
|
+
|
46
|
+
store.to_s.downcase.to_sym
|
47
|
+
end
|
19
48
|
end
|
20
49
|
end
|
21
50
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Passwordstate
|
2
4
|
module Resources
|
3
5
|
class Folder < Passwordstate::Resource
|
@@ -16,7 +18,8 @@ module Passwordstate
|
|
16
18
|
alias title folder_name
|
17
19
|
|
18
20
|
def password_lists
|
19
|
-
Passwordstate::ResourceList.new
|
21
|
+
Passwordstate::ResourceList.new Passwordstate::Resources::PasswordList,
|
22
|
+
client: client,
|
20
23
|
search_query: { tree_path: tree_path },
|
21
24
|
all_path: 'searchpasswordlists',
|
22
25
|
all_query: { tree_path: tree_path },
|
@@ -28,7 +31,7 @@ module Passwordstate
|
|
28
31
|
FolderPermission.new(_client: client, folder_id: folder_id)
|
29
32
|
end
|
30
33
|
|
31
|
-
def full_path(unix
|
34
|
+
def full_path(unix: false)
|
32
35
|
return tree_path.tr('\\', '/') if unix
|
33
36
|
|
34
37
|
tree_path
|
@@ -40,7 +43,7 @@ module Passwordstate
|
|
40
43
|
|
41
44
|
index_field :folder_id
|
42
45
|
|
43
|
-
read_fields :folder_id, { name: 'FolderID' }
|
46
|
+
read_fields :folder_id, { name: 'FolderID' }
|
44
47
|
end
|
45
48
|
end
|
46
49
|
end
|