passwordstate 0.1.0 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ead3a53cdb04468a85069d55f1e6764562ea518dfb1ccb2e0b640c883ecfc85f
4
- data.tar.gz: 4d4dee12f8a862302b80c7fdb2027a0fb98bfe0a7ab45ece68622a6e42a9ed6a
3
+ metadata.gz: 8339c30925359da2d1fb10918796945cb5bdc4ad56beff7e99af39781bdfae96
4
+ data.tar.gz: 6d9c0c54ce250813c874fa7007c63e8378c9c20ac4a6b9df887b6fe74b5939b9
5
5
  SHA512:
6
- metadata.gz: f306e2cf53329fc8f83f6364d02ec940a3df971f1f9b79e9e9866f40084c75e1fd96aeef42bca640f98e7507bb799006603f2007cf3cd01e5004eaeb93695ff9
7
- data.tar.gz: 1f744f7a0f2bdd43b536b71de33bd218f1d496b79fcbfc4d0d284afdf9f80b35307c2acc21165e94d692268c22203ba1adb0a470ef76ba246c6292458cd971a2
6
+ metadata.gz: 7cbe2c67683aa901f0d4b3fc4c9991115c59f93ecef0b1b8436e78c0f752abcf71c60a2f8151df7714e0d872d9620402a2fc1273d30875f70648916f8f5459ab
7
+ data.tar.gz: 5feb809a4426576eb901a18c137f0d70337a692ef7cfdc633122ca1d0042d7b59f1727841f8b32ff68186ff0cc93cf4fa895de21622b250d5d9c92341d44a709
data/.gitlab-ci.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- image: "ruby:2.7"
2
+ image: "ruby"
3
3
 
4
4
  # Cache gems in between builds
5
5
  cache:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## v0.1.2 2025-08-26
2
+
3
+ - Improved performance when working with large lists
4
+
5
+ ## v0.1.1 2024-03-27
6
+
7
+ - Add Client#open_timeout=
8
+ - Add Resource#modified?
9
+
1
10
  ## v0.1.0 2022-12-05
2
11
 
3
12
  - Reduced data transferred for regular queries
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
+ require 'pp' # rubocop:disable Lint/RedundantRequireStatement - required for pretty_print_inspect
4
5
 
5
6
  module Passwordstate
6
7
  class Client
@@ -11,7 +12,7 @@ module Passwordstate
11
12
  }.freeze
12
13
 
13
14
  attr_accessor :server_url, :auth_data, :headers, :validate_certificate
14
- attr_reader :timeout
15
+ attr_reader :open_timeout, :timeout
15
16
  attr_writer :api_type
16
17
 
17
18
  def initialize(url, options = {})
@@ -20,6 +21,7 @@ module Passwordstate
20
21
  @headers = DEFAULT_HEADERS
21
22
  @auth_data = options.select { |k, _v| %i[apikey username password].include? k }
22
23
  @api_type = options.fetch(:api_type) if options.key? :api_type
24
+ @open_timeout = options.fetch(:open_timeout, 5)
23
25
  @timeout = options.fetch(:timeout, 15)
24
26
  end
25
27
 
@@ -31,6 +33,11 @@ module Passwordstate
31
33
  @api_type || (auth_data.key?(:apikey) ? :api : :winapi)
32
34
  end
33
35
 
36
+ def open_timeout=(sec)
37
+ @open_timeout = sec
38
+ @http.open_timeout = sec if @http
39
+ end
40
+
34
41
  def timeout=(sec)
35
42
  @timeout = sec
36
43
  @http.read_timeout = sec if @http
@@ -155,6 +162,7 @@ module Passwordstate
155
162
  @http ||= Net::HTTP.new server_url.host, server_url.port
156
163
  return @http if @http.active?
157
164
 
165
+ @http.open_timeout = @open_timeout if @open_timeout
158
166
  @http.read_timeout = @timeout if @timeout
159
167
  @http.use_ssl = server_url.scheme == 'https'
160
168
  @http.verify_mode = validate_certificate ? ::OpenSSL::SSL::VERIFY_NONE : nil
@@ -12,10 +12,14 @@ module Passwordstate
12
12
  @code = code.to_i
13
13
  @request = request
14
14
  @response = response
15
+
16
+ if errors.count == 1 && errors.first.is_a?(Hash) && errors.first.key?('errors')
17
+ errors = errors.first['errors']
18
+ end
15
19
  @errors = errors
16
20
 
17
21
  errorstr = errors.map { |err| err['message'] || err['phrase'] || err['error'] }.join('; ')
18
- super "Passwordstate responded with an error to the request:\n#{errorstr}"
22
+ super("Passwordstate responded with an error to the request:\n#{errorstr}")
19
23
  end
20
24
 
21
25
  def self.new_by_code(code, req, res, errors = [])
@@ -34,8 +34,7 @@ module Passwordstate
34
34
 
35
35
  def initialize(data)
36
36
  @client = data.delete :_client
37
- set! data, store_old: false
38
- old
37
+ set! data
39
38
  end
40
39
 
41
40
  # Is the object stored on the Passwordstate server
@@ -126,7 +125,7 @@ module Passwordstate
126
125
  nil_as_string = opts.fetch(:nil_as_string, self.class.nil_as_string)
127
126
  (self.class.send(:accessor_field_names) + self.class.send(:read_field_names) + self.class.send(:write_field_names)).to_h do |field|
128
127
  redact = self.class.send(:field_options)[field]&.fetch(:redact, false) && !ignore_redact
129
- at_field = "@#{field}".to_sym
128
+ at_field = :"@#{field}"
130
129
  field = at_field if atify
131
130
  value = instance_variable_get(at_field) unless redact
132
131
  value = '[ REDACTED ]' if redact
@@ -151,6 +150,12 @@ module Passwordstate
151
150
  end
152
151
  end
153
152
 
153
+ def modified?(field = nil)
154
+ return modified.any? unless field
155
+
156
+ modified.include? field
157
+ end
158
+
154
159
  protected
155
160
 
156
161
  def api_path
@@ -158,20 +163,11 @@ module Passwordstate
158
163
  end
159
164
 
160
165
  def modified
161
- attribs = attributes
162
- attribs.reject { |field| old[field] == attribs[field] }
166
+ @modified ||= {}
163
167
  end
164
168
 
165
- def modified?(field)
166
- modified.include? field
167
- end
168
-
169
- def old
170
- @old ||= attributes.dup
171
- end
172
-
173
- def set!(data, store_old: true)
174
- @old = attributes.dup if store_old
169
+ def set!(data, reset_modified = true)
170
+ @modified = {} if reset_modified
175
171
  data = data.attributes if data.is_a? Passwordstate::Resource
176
172
  data.each do |key, value|
177
173
  field = self.class.passwordstate_to_ruby_field(key)
@@ -188,7 +184,7 @@ module Passwordstate
188
184
  parsed_value = convert.call(value, direction: :from)
189
185
  end
190
186
 
191
- instance_variable_set "@#{field}".to_sym, parsed_value || value
187
+ instance_variable_set :"@#{field}", parsed_value || value
192
188
  end
193
189
  self
194
190
  end
@@ -272,7 +268,8 @@ module Passwordstate
272
268
  fields.each do |field|
273
269
  if field.is_a? Symbol
274
270
  accessor_field_names << field
275
- attr_accessor field
271
+ attr_reader field
272
+ build_writer_method field
276
273
  else
277
274
  field_options[accessor_field_names.last] = field
278
275
  end
@@ -294,12 +291,24 @@ module Passwordstate
294
291
  fields.each do |field|
295
292
  if field.is_a? Symbol
296
293
  write_field_names << field
297
- attr_writer field
294
+ build_writer_method field
298
295
  else
299
296
  field_options[write_field_names.last] = field
300
297
  end
301
298
  end
302
299
  end
300
+
301
+ private
302
+
303
+ def build_writer_method(field)
304
+ field = field.to_s.to_sym unless field.is_a? Symbol
305
+ define_method(:"#{field}=") do |arg|
306
+ return arg if send(field) == arg
307
+
308
+ instance_variable_set "@#{field}", arg
309
+ modified[field] = arg
310
+ end
311
+ end
303
312
  end
304
313
  end
305
314
  # rubocop:enable Metrics/ClassLength
@@ -315,7 +324,7 @@ module Passwordstate
315
324
  autoload :PasswordList, 'passwordstate/resources/password_list'
316
325
  autoload :PasswordListPermission, 'passwordstate/resources/password_list'
317
326
  autoload :PasswordHistory, 'passwordstate/resources/password'
318
- autoload :PasswordPermission, 'passwordstate/resources/password_list'
327
+ autoload :PasswordPermission, 'passwordstate/resources/password'
319
328
  autoload :PrivilegedAccount, 'passwordstate/resources/privileged_account'
320
329
  autoload :PrivilegedAccountPermission, 'passwordstate/resources/privileged_account'
321
330
  autoload :Permission, 'passwordstate/resources/permission'
@@ -62,11 +62,11 @@ module Passwordstate
62
62
  end
63
63
 
64
64
  def operation_supported?(operation)
65
- return nil unless %i[search all get post put delete].include?(operation)
65
+ return false unless %i[search all get post put delete].include?(operation)
66
66
  return false if options.key?(:only) && !options[:only].include?(operation)
67
67
  return false if options.key?(:except) && options[:except].include?(operation)
68
68
 
69
- !options.fetch("#{operation}_path".to_sym, '').nil?
69
+ !options.fetch(:"#{operation}_path", '').nil?
70
70
  end
71
71
 
72
72
  def new(data)
@@ -13,7 +13,7 @@ module Passwordstate
13
13
  alias title document_name
14
14
 
15
15
  def self.all(client, store, **query)
16
- super client, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
16
+ super(client, query.merge(_api_path: "#{api_path}/#{validate_store! store}"))
17
17
  end
18
18
 
19
19
  def self.search(client, store, **options)
@@ -21,19 +21,19 @@ module Passwordstate
21
21
  end
22
22
 
23
23
  def self.get(client, store, object)
24
- super client, object, _api_path: "#{api_path}/#{validate_store! store}"
24
+ super(client, object, _api_path: "#{api_path}/#{validate_store! store}")
25
25
  end
26
26
 
27
27
  def self.post(client, store, data, **query)
28
- super client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
28
+ super(client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}"))
29
29
  end
30
30
 
31
31
  def self.put(client, store, data, **query)
32
- super client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
32
+ super(client, data, query.merge(_api_path: "#{api_path}/#{validate_store! store}"))
33
33
  end
34
34
 
35
35
  def self.delete(client, store, object, **query)
36
- super client, object, query.merge(_api_path: "#{api_path}/#{validate_store! store}")
36
+ super(client, object, query.merge(_api_path: "#{api_path}/#{validate_store! store}"))
37
37
  end
38
38
 
39
39
  class << self
@@ -112,11 +112,13 @@ module Passwordstate
112
112
  end
113
113
 
114
114
  def self.all(client, **query)
115
- super client, **{ query_all: true }.merge(query)
115
+ query = { query_all: true }.merge(query)
116
+ super(client, **query)
116
117
  end
117
118
 
118
119
  def self.search(client, **query)
119
- super client, **{ _api_path: 'searchpasswords' }.merge(query)
120
+ query = { _api_path: 'searchpasswords' }.merge(query)
121
+ super(client, **query)
120
122
  end
121
123
 
122
124
  def self.generate(client, **options)
@@ -70,7 +70,7 @@ module Passwordstate
70
70
  alias title password_list
71
71
 
72
72
  def self.search(client, **query)
73
- super client, **query.merge(_api_path: 'searchpasswordlists')
73
+ super(client, **query.merge(_api_path: 'searchpasswordlists'))
74
74
  end
75
75
 
76
76
  def passwords
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Passwordstate
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
data/lib/passwordstate.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'logging'
4
- require 'pp'
5
4
  require 'passwordstate/version'
6
5
  require 'passwordstate/client'
7
6
  require 'passwordstate/errors'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passwordstate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Olofsson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-12-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: logging
@@ -173,7 +172,6 @@ licenses:
173
172
  - MIT
174
173
  metadata:
175
174
  rubygems_mfa_required: 'true'
176
- post_install_message:
177
175
  rdoc_options: []
178
176
  require_paths:
179
177
  - lib
@@ -188,8 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
188
186
  - !ruby/object:Gem::Version
189
187
  version: '0'
190
188
  requirements: []
191
- rubygems_version: 3.3.8
192
- signing_key:
189
+ rubygems_version: 3.6.9
193
190
  specification_version: 4
194
191
  summary: A ruby API client for interacting with a passwordstate server
195
192
  test_files: []