passwordstate 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +54 -15
- data/lib/passwordstate/errors.rb +6 -1
- data/lib/passwordstate/resource.rb +131 -49
- 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,56 +81,82 @@ 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] }]
|
88
|
-
end
|
89
|
-
|
90
|
-
def api_path
|
91
|
-
self.class.instance_variable_get :@api_path
|
117
|
+
client.request :delete, "#{path}/#{object}", query: query, reason: reason
|
92
118
|
end
|
93
119
|
|
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
|
108
152
|
end
|
109
153
|
|
110
154
|
protected
|
111
155
|
|
156
|
+
def api_path
|
157
|
+
self.class.instance_variable_get :@api_path
|
158
|
+
end
|
159
|
+
|
112
160
|
def modified
|
113
161
|
attribs = attributes
|
114
162
|
attribs.reject { |field| old[field] == attribs[field] }
|
@@ -122,7 +170,7 @@ module Passwordstate
|
|
122
170
|
@old ||= attributes.dup
|
123
171
|
end
|
124
172
|
|
125
|
-
def set!(data, store_old
|
173
|
+
def set!(data, store_old: true)
|
126
174
|
@old = attributes.dup if store_old
|
127
175
|
data = data.attributes if data.is_a? Passwordstate::Resource
|
128
176
|
data.each do |key, value|
|
@@ -133,8 +181,11 @@ module Passwordstate
|
|
133
181
|
|
134
182
|
if !value.nil? && opts&.key?(:is)
|
135
183
|
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? :
|
184
|
+
parsed_value = klass.send :parse, value rescue nil if klass.respond_to?(:parse)
|
185
|
+
parsed_value ||= klass.send :new, value rescue nil if klass.respond_to?(:new) && !klass.respond_to?(:parse)
|
186
|
+
elsif !value.nil? && opts&.key?(:convert)
|
187
|
+
convert = opts.fetch(:convert)
|
188
|
+
parsed_value = convert.call(value, direction: :from)
|
138
189
|
end
|
139
190
|
|
140
191
|
instance_variable_set "@#{field}".to_sym, parsed_value || value
|
@@ -145,26 +196,52 @@ module Passwordstate
|
|
145
196
|
class << self
|
146
197
|
alias search all
|
147
198
|
|
199
|
+
# Get/Set the API path for the resource type
|
148
200
|
def api_path(path = nil)
|
149
201
|
@api_path = path unless path.nil?
|
150
202
|
@api_path
|
151
203
|
end
|
152
204
|
|
205
|
+
# Get/Set the index field for the resource type
|
153
206
|
def index_field(field = nil)
|
154
207
|
@index_field = field unless field.nil?
|
155
208
|
@index_field
|
156
209
|
end
|
157
210
|
|
211
|
+
# Get/Set whether the resource type requires nil values to be handled as the empty string
|
158
212
|
def nil_as_string(opt = nil)
|
159
213
|
@nil_as_string = opt unless opt.nil?
|
160
214
|
@nil_as_string
|
161
215
|
end
|
162
216
|
|
217
|
+
# Get/Set acceptable CRUD methods for the resource type
|
218
|
+
def acceptable_methods(*meths)
|
219
|
+
if meths.empty?
|
220
|
+
@acceptable_methods || %i[post get put delete]
|
221
|
+
elsif meths.count == 1 && meths.to_s.upcase == meths.to_s
|
222
|
+
@acceptable_methods = []
|
223
|
+
meths = meths.first.to_s
|
224
|
+
@acceptable_methods << :post if meths.include? 'C'
|
225
|
+
@acceptable_methods << :get if meths.include? 'R'
|
226
|
+
@acceptable_methods << :put if meths.include? 'U'
|
227
|
+
@acceptable_methods << :delete if meths.include? 'D'
|
228
|
+
else
|
229
|
+
@acceptable_methods = meths
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Convert a hash from Ruby syntax to Passwordstate syntax
|
234
|
+
def passwordstateify_hash(hash)
|
235
|
+
hash.transform_keys { |k| ruby_to_passwordstate_field(k) }
|
236
|
+
end
|
237
|
+
|
238
|
+
# Convert a Passwordstate field name into Ruby syntax
|
163
239
|
def passwordstate_to_ruby_field(field)
|
164
240
|
opts = send(:field_options).find { |(_k, v)| v[:name] == field }
|
165
241
|
opts&.first || field.to_s.snake_case.to_sym
|
166
242
|
end
|
167
243
|
|
244
|
+
# Convert a Ruby field name into Passwordstate syntax
|
168
245
|
def ruby_to_passwordstate_field(field)
|
169
246
|
send(:field_options)[field]&.[](:name) || field.to_s.camel_case
|
170
247
|
end
|
@@ -225,18 +302,23 @@ module Passwordstate
|
|
225
302
|
end
|
226
303
|
end
|
227
304
|
end
|
305
|
+
# rubocop:enable Metrics/ClassLength
|
228
306
|
|
229
307
|
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 :
|
308
|
+
autoload :ActiveDirectory, 'passwordstate/resources/active_directory'
|
309
|
+
autoload :AddressBook, 'passwordstate/resources/address_book'
|
310
|
+
autoload :Document, 'passwordstate/resources/document'
|
311
|
+
autoload :Folder, 'passwordstate/resources/folder'
|
312
|
+
autoload :FolderPermission, 'passwordstate/resources/folder'
|
313
|
+
autoload :Host, 'passwordstate/resources/host'
|
314
|
+
autoload :Password, 'passwordstate/resources/password'
|
315
|
+
autoload :PasswordList, 'passwordstate/resources/password_list'
|
316
|
+
autoload :PasswordListPermission, 'passwordstate/resources/password_list'
|
317
|
+
autoload :PasswordHistory, 'passwordstate/resources/password'
|
318
|
+
autoload :PasswordPermission, 'passwordstate/resources/password_list'
|
319
|
+
autoload :PrivilegedAccount, 'passwordstate/resources/privileged_account'
|
320
|
+
autoload :PrivilegedAccountPermission, 'passwordstate/resources/privileged_account'
|
321
|
+
autoload :Permission, 'passwordstate/resources/permission'
|
322
|
+
autoload :Report, 'passwordstate/resources/report'
|
241
323
|
end
|
242
324
|
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
|