passwordstate 0.0.2 → 0.0.3

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: e3e65ba47af619efcf537b2636eb297ac1cbe7a1c423c323e44a0126f27a5e2e
4
- data.tar.gz: 5829c7ee7bf3af1d9098246e16f53f06f29d2002683cf8aed15b87a900e50bec
3
+ metadata.gz: 1fb957850976d1356b56000ef102e03638ff25ded2fc7d978ec9cd4399a51b4d
4
+ data.tar.gz: 2dc78e6bd303d713bc5e61e2fdfc608f1d65e826edbe04e7bb17698fdc6e88af
5
5
  SHA512:
6
- metadata.gz: 96b62d8b3701e7779c035f7bdb05cf4b6ab5540fa74749742169bde51e093ccbe2d255c50309965c3f056bd5081fb24e438dd3f04e096a55cee12e3301b755a1
7
- data.tar.gz: 5ed0bc2e7d0c7f922cb1ceee760d3a090381afda9a952d9fb98919eaedc1eee7cb1fecd5d18d3e65f28f6be5564a3eda13f35a8cd0e6a5f25ef8e10b26671b24
6
+ metadata.gz: 73027a2b795d5611b76c5733bc5ec1cbcde1de34cc956e48f3bc52c22f473df5d9ce0dcc54540688ef7481669d61db185711b64ada6f1d04528ff459dbd6c9bb
7
+ data.tar.gz: 6a9bfb4fd0d9f9579876334c219f0403fdb35a3dbf871bb0a884c9de07f86f7f95cdde9cd1434cdba86b0adbabe8c4613e8a30beab0d49db6ca404d741713f1f
@@ -7,7 +7,7 @@ cache:
7
7
  - vendor/ruby
8
8
 
9
9
  before_script:
10
- - gem install bundler --no-ri --no-rdoc
10
+ - gem install bundler -N
11
11
  - bundle install -j $(nproc) --path vendor
12
12
 
13
13
  rubocop:
@@ -1,7 +1,16 @@
1
+ ## v0.0.3 2019-10-23
2
+
3
+ - Added method to check if resource types are available
4
+ - Added `_bare: true` flag on resource getter to create a bare object for
5
+ method calls
6
+ - Fixed handling of host objects
7
+ - Further improved exception handling
8
+
1
9
  ## v0.0.2 2018-08-14
2
10
 
3
- - Add title and full_path fields to the appropriate resources - almost every resource should now have an obvious human-readable name
4
- - Fix password searching in password lists
11
+ - Added title and full_path fields to the appropriate resources - almost every
12
+ resource should now have an obvious human-readable name
13
+ - Fixed password searching in password lists
5
14
  - Improved exception handling
6
15
 
7
16
  ## v0.0.1 2018-07-31
@@ -4,11 +4,12 @@ module Passwordstate
4
4
  class Client
5
5
  USER_AGENT = "RubyPasswordstate/#{Passwordstate::VERSION}".freeze
6
6
  DEFAULT_HEADERS = {
7
- 'accept' => 'application/json',
8
- 'user-agent' => USER_AGENT
7
+ 'accept' => 'application/json',
8
+ 'user-agent' => USER_AGENT
9
9
  }.freeze
10
10
 
11
11
  attr_accessor :server_url, :auth_data, :headers, :validate_certificate
12
+ attr_reader :timeout
12
13
  attr_writer :api_type
13
14
 
14
15
  def initialize(url, options = {})
@@ -17,6 +18,7 @@ module Passwordstate
17
18
  @headers = DEFAULT_HEADERS
18
19
  @auth_data = options.select { |k, _v| %i[apikey username password].include? k }
19
20
  @api_type = options.fetch(:api_type) if options.key? :api_type
21
+ @timeout = options.fetch(:timeout, 15)
20
22
  end
21
23
 
22
24
  def logger
@@ -27,6 +29,11 @@ module Passwordstate
27
29
  @api_type || (auth_data.key?(:apikey) ? :api : :winapi)
28
30
  end
29
31
 
32
+ def timeout=(sec)
33
+ @timeout = sec
34
+ @http.read_timeout = sec if @http
35
+ end
36
+
30
37
  def folders
31
38
  ResourceList.new self, Passwordstate::Resources::Folder,
32
39
  only: %i[all search post]
@@ -34,7 +41,7 @@ module Passwordstate
34
41
 
35
42
  def hosts
36
43
  ResourceList.new self, Passwordstate::Resources::Host,
37
- only: %i[search post delete]
44
+ except: %i[search put]
38
45
  end
39
46
 
40
47
  def passwords
@@ -62,10 +69,18 @@ module Passwordstate
62
69
  end
63
70
  end
64
71
 
72
+ def version?(compare)
73
+ Gem::Dependency.new(to_s, compare).match?(to_s, version)
74
+ end
75
+
76
+ def require_version(compare)
77
+ raise "Your version of Passwordstate (#{version}) doesn't support the requested feature" unless version? compare
78
+ end
79
+
65
80
  def request(method, api_path, options = {})
66
81
  uri = URI(server_url + "/#{api_type}/" + api_path)
67
82
  uri.query = URI.encode_www_form(options.fetch(:query)) if options.key? :query
68
- uri.query = nil if uri.query&.empty?
83
+ uri.query = nil unless uri.query&.any?
69
84
 
70
85
  req_obj = Net::HTTP.const_get(method.to_s.capitalize.to_sym).new uri
71
86
  if options.key? :body
@@ -77,6 +92,7 @@ module Passwordstate
77
92
  req_obj.ntlm_auth(auth_data[:username], auth_data[:password]) if api_type == :winapi
78
93
  headers.each { |h, v| req_obj[h] = v }
79
94
  req_obj['APIKey'] = auth_data[:apikey] if api_type == :api
95
+ req_obj['Reason'] = options.fetch(:reason) if options.key?(:reason) && version?('>= 8.4.8449')
80
96
 
81
97
  print_http req_obj
82
98
  res_obj = http.request req_obj
@@ -87,12 +103,14 @@ module Passwordstate
87
103
  data = JSON.parse(res_obj.body) rescue nil
88
104
  if data
89
105
  return data if res_obj.is_a? Net::HTTPSuccess
106
+
90
107
  data = data&.first
91
108
 
92
109
  raise Passwordstate::HTTPError.new_by_code(res_obj.code, req_obj, res_obj, data&.fetch('errors', []) || [])
93
110
  else
94
- return res_obj.body if options.fetch(:allow_html, false)
95
- raise Passwordstate::PasswordstateError, 'Response was not parseable as JSON'
111
+ return res_obj.body if res_obj.is_a?(Net::HTTPSuccess) && options.fetch(:allow_html, true)
112
+
113
+ raise Passwordstate::HTTPError.new_by_code(res_obj.code, req_obj, res_obj, [{ 'message' => res_obj.body }])
96
114
  end
97
115
  end
98
116
 
@@ -106,6 +124,7 @@ module Passwordstate
106
124
  @http ||= Net::HTTP.new server_url.host, server_url.port
107
125
  return @http if @http.active?
108
126
 
127
+ @http.read_timeout = @timeout if @timeout
109
128
  @http.use_ssl = server_url.scheme == 'https'
110
129
  @http.verify_mode = validate_certificate ? ::OpenSSL::SSL::VERIFY_NONE : nil
111
130
  @http.start
@@ -127,6 +146,7 @@ module Passwordstate
127
146
  logger.debug dir
128
147
 
129
148
  return if http.body.nil?
149
+
130
150
  clean_body = JSON.parse(http.body) rescue nil
131
151
  if clean_body
132
152
  clean_body = clean_body.each { |k, v| v.replace('[ REDACTED ]') if k.is_a?(String) && %w[password apikey].include?(k.downcase) }.to_json if http.body
@@ -10,20 +10,24 @@ module Passwordstate
10
10
  @response = response
11
11
  @errors = errors
12
12
 
13
- super <<-ERRMSG
14
- Passwordstate responded with an error to the request;
15
- #{errors.map { |err| err['message'] || err['phrase'] }.join(', ')}
16
- ERRMSG
13
+ super "Passwordstate responded with an error to the request:\n#{errors.map { |err| err['message'] || err['phrase'] }.join('; ')}"
17
14
  end
18
15
 
19
16
  def self.new_by_code(code, req, res, errors = [])
20
17
  code_i = code.to_i
21
18
 
22
19
  errtype = nil
20
+ errtype ||= UnauthorizedError if code_i == 401
21
+ errtype ||= ForbiddenError if code_i == 403
23
22
  errtype ||= NotFoundError if code_i == 404
24
23
  errtype ||= ClientError if code_i >= 400 && code_i < 500
25
24
  errtype ||= ServerError if code_i >= 500 && code_i < 600
26
25
 
26
+ if code_i == 302 && res['location'].start_with?('/error/generalerror.aspx?')
27
+ errtype ||= ServerError
28
+ errors = [{ 'phrase' => 'Response code 302, most likely meaning an authorization error' }]
29
+ end
30
+
27
31
  errtype ||= HTTPError
28
32
  errtype.new(code_i, req, res, errors)
29
33
  end
@@ -36,11 +40,16 @@ ERRMSG
36
40
  end
37
41
  end
38
42
 
43
+ # 401
44
+ class UnauthorizedError < ClientError
45
+ end
46
+
47
+ # 403
48
+ class ForbiddenError < ClientError
49
+ end
50
+
39
51
  # 404
40
52
  class NotFoundError < ClientError
41
- def initialize(code, req, res, errors = [])
42
- super
43
- end
44
53
  end
45
54
 
46
55
  # 5xx
@@ -30,6 +30,10 @@ module Passwordstate
30
30
  !send(self.class.index_field).nil?
31
31
  end
32
32
 
33
+ def self.available?(_client)
34
+ true
35
+ end
36
+
33
37
  def self.all(client, query = {})
34
38
  path = query.fetch(:_api_path, api_path)
35
39
  query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
@@ -40,14 +44,18 @@ module Passwordstate
40
44
  end
41
45
 
42
46
  def self.get(client, object, query = {})
47
+ object = object.send(object.class.send(index_field)) if object.is_a? Resource
48
+
49
+ return new _client: client, index_field => object if query[:_bare]
50
+
43
51
  path = query.fetch(:_api_path, api_path)
44
52
  query = passwordstateify_hash query.reject { |k| k.to_s.start_with? '_' }
45
53
 
46
- object = object.send(object.class.send(index_field)) if object.is_a? Resource
47
54
  resp = client.request(:get, "#{path}/#{object}", query: query).map do |data|
48
55
  new data.merge(_client: client)
49
56
  end
50
57
  return resp.first if resp.one? || resp.empty?
58
+
51
59
  resp
52
60
  end
53
61
 
@@ -83,17 +91,20 @@ module Passwordstate
83
91
  self.class.instance_variable_get :@api_path
84
92
  end
85
93
 
86
- def attributes(ignore_redact = true)
94
+ def attributes(opts = {})
95
+ ignore_redact = opts.fetch(:ignore_redact, true)
96
+ nil_as_string = opts.fetch(:nil_as_string, self.class.nil_as_string)
87
97
  Hash[(self.class.send(:accessor_field_names) + self.class.send(:read_field_names) + self.class.send(:write_field_names)).map do |field|
88
98
  redact = self.class.send(:field_options)[field]&.fetch(:redact, false) && !ignore_redact
89
99
  value = instance_variable_get("@#{field}".to_sym) unless redact
90
100
  value = '[ REDACTED ]' if redact
101
+ value = '' if value.nil? && nil_as_string
91
102
  [field, value]
92
103
  end].reject { |_k, v| v.nil? }
93
104
  end
94
105
 
95
106
  def inspect
96
- "#{to_s[0..-2]} #{attributes(false).reject { |_k, v| v.nil? }.map { |k, v| "@#{k}=#{v.inspect}" }.join(', ')}>"
107
+ "#{to_s[0..-2]} #{attributes(nil_as_string: false, ignore_redact: false).reject { |_k, v| v.nil? }.map { |k, v| "@#{k}=#{v.inspect}" }.join(', ')}>"
97
108
  end
98
109
 
99
110
  protected
@@ -144,6 +155,11 @@ module Passwordstate
144
155
  @index_field
145
156
  end
146
157
 
158
+ def nil_as_string(opt = nil)
159
+ @nil_as_string = opt unless opt.nil?
160
+ @nil_as_string
161
+ end
162
+
147
163
  def passwordstate_to_ruby_field(field)
148
164
  opts = send(:field_options).find { |(_k, v)| v[:name] == field }
149
165
  opts&.first || field.to_s.snake_case.to_sym
@@ -211,12 +227,16 @@ module Passwordstate
211
227
  end
212
228
 
213
229
  module Resources
214
- autoload :Document, 'passwordstate/resources/document'
215
- autoload :Folder, 'passwordstate/resources/folder'
216
- autoload :Host, 'passwordstate/resources/host'
217
- autoload :PasswordList, 'passwordstate/resources/password_list'
218
- autoload :Password, 'passwordstate/resources/password'
219
- autoload :PasswordHistory, 'passwordstate/resources/password'
220
- autoload :Report, 'passwordstate/resources/report'
230
+ autoload :Document, 'passwordstate/resources/document'
231
+ autoload :Folder, 'passwordstate/resources/folder'
232
+ autoload :FolderPermission, 'passwordstate/resources/folder'
233
+ autoload :Host, 'passwordstate/resources/host'
234
+ autoload :PasswordList, 'passwordstate/resources/password_list'
235
+ autoload :PasswordListPermission, 'passwordstate/resources/password_list'
236
+ autoload :Password, 'passwordstate/resources/password'
237
+ autoload :PasswordHistory, 'passwordstate/resources/password'
238
+ autoload :PasswordPermission, 'passwordstate/resources/password_list'
239
+ autoload :Permission, 'passwordstate/resources/permission'
240
+ autoload :Report, 'passwordstate/resources/report'
221
241
  end
222
242
  end
@@ -2,6 +2,7 @@ module Passwordstate
2
2
  class ResourceList < Array
3
3
  Array.public_instance_methods(false).each do |method|
4
4
  next if %i[reject select slice clear inspect].include?(method.to_sym)
5
+
5
6
  class_eval <<-EVAL, __FILE__, __LINE__ + 1
6
7
  def #{method}(*args)
7
8
  lazy_load unless @loaded
@@ -48,7 +49,7 @@ module Passwordstate
48
49
 
49
50
  def load(entries)
50
51
  clear && entries.tap do |loaded|
51
- loaded.sort! { |obj| obj.send(obj.class.index_field) } if options.fetch(:sort, true)
52
+ loaded.sort! { |a, b| a.send(a.class.index_field) <=> b.send(b.class.index_field) } if options.fetch(:sort, true)
52
53
  end.each { |obj| self << obj }
53
54
  self
54
55
  end
@@ -57,6 +58,7 @@ module Passwordstate
57
58
  return nil unless %i[search all get post put delete].include?(operation)
58
59
  return false if options.key?(:only) && !options[:only].include?(operation)
59
60
  return false if options.key?(:except) && options[:except].include?(operation)
61
+
60
62
  !options.fetch("#{operation}_path".to_sym, '').nil?
61
63
  end
62
64
 
@@ -66,6 +68,7 @@ module Passwordstate
66
68
 
67
69
  def create(data)
68
70
  raise 'Operation not supported' unless operation_supported?(:post)
71
+
69
72
  obj = resource.new options.fetch(:object_data, {}).merge(data).merge(_client: client)
70
73
  obj.post
71
74
  obj
@@ -73,6 +76,7 @@ module Passwordstate
73
76
 
74
77
  def search(query = {})
75
78
  raise 'Operation not supported' unless operation_supported?(:search)
79
+
76
80
  api_path = options.fetch(:search_path, resource.api_path)
77
81
  query = options.fetch(:search_query, {}).merge(query)
78
82
 
@@ -81,6 +85,7 @@ module Passwordstate
81
85
 
82
86
  def all(query = {})
83
87
  raise 'Operation not supported' unless operation_supported?(:all)
88
+
84
89
  api_path = options.fetch(:all_path, resource.api_path)
85
90
  query = options.fetch(:all_query, {}).merge(query)
86
91
 
@@ -89,6 +94,7 @@ module Passwordstate
89
94
 
90
95
  def get(id, query = {})
91
96
  raise 'Operation not supported' unless operation_supported?(:get)
97
+
92
98
  api_path = options.fetch(:get_path, resource.api_path)
93
99
  query = options.fetch(:get_query, {}).merge(query)
94
100
 
@@ -97,6 +103,7 @@ module Passwordstate
97
103
 
98
104
  def post(data, query = {})
99
105
  raise 'Operation not supported' unless operation_supported?(:post)
106
+
100
107
  api_path = options.fetch(:post_path, resource.api_path)
101
108
  query = options.fetch(:post_query, {}).merge(query)
102
109
 
@@ -105,6 +112,7 @@ module Passwordstate
105
112
 
106
113
  def put(data, query = {})
107
114
  raise 'Operation not supported' unless operation_supported?(:put)
115
+
108
116
  api_path = options.fetch(:put_path, resource.api_path)
109
117
  query = options.fetch(:put_query, {}).merge(query)
110
118
 
@@ -113,6 +121,7 @@ module Passwordstate
113
121
 
114
122
  def delete(id, query = {})
115
123
  raise 'Operation not supported' unless operation_supported?(:delete)
124
+
116
125
  api_path = options.fetch(:delete_path, resource.api_path)
117
126
  query = options.fetch(:delete_query, {}).merge(query)
118
127
 
@@ -23,10 +23,24 @@ module Passwordstate
23
23
  object_data: { nest_undef_folder_id: folder_id }
24
24
  end
25
25
 
26
+ def permissions
27
+ client.require_version('>= 8.4.8449')
28
+ FolderPermission.new(_client: client, folder_id: folder_id)
29
+ end
30
+
26
31
  def full_path(unix = false)
27
32
  return tree_path.tr('\\', '/') if unix
33
+
28
34
  tree_path
29
35
  end
30
36
  end
37
+
38
+ class FolderPermission < Permission
39
+ api_path 'folderpermissions'
40
+
41
+ index_field :folder_id
42
+
43
+ read_fields :folder_id, { name: 'FolderID' } # rubocop:disable Style/BracesAroundHashParameters
44
+ end
31
45
  end
32
46
  end
@@ -18,6 +18,7 @@ module Passwordstate
18
18
  :remote_connection_parameters,
19
19
  :tag,
20
20
  :title,
21
+ :discovery_job_id, { name: 'DiscoveryJobID' },
21
22
  :site_id, { name: 'SiteID' },
22
23
  :internal_ip, { name: 'InternalIP', is: IPAddr },
23
24
  :external_ip, { name: 'ExternalIP', is: IPAddr },
@@ -26,7 +27,20 @@ module Passwordstate
26
27
  :virtual_machine_type,
27
28
  :notes
28
29
 
29
- read_fields :host_id, { name: 'HostID' } # rubocop:disable Style/BracesAroundHashParameters
30
+ read_fields :host_id, { name: 'HostID' },
31
+ :site_location
32
+
33
+ # TODO: API breaks if all fields aren't included
34
+ nil_as_string true
35
+
36
+ def self.available?(client)
37
+ client.request :get, api_path
38
+ true
39
+ rescue Passwordstate::NotFoundError
40
+ true
41
+ rescue Passwordstate::ForbiddenError
42
+ false
43
+ end
30
44
  end
31
45
  end
32
46
  end
@@ -21,16 +21,18 @@ module Passwordstate
21
21
  :generic_field_9, { name: 'GenericField9' },
22
22
  :generic_field_10, { name: 'GenericField10' },
23
23
  :account_type_id, { name: 'AccountTypeID' },
24
- :account_type,
25
24
  :notes,
26
25
  :url,
27
26
  :password, { redact: true },
28
27
  :expiry_date, { is: Time },
29
28
  :allow_export,
30
29
  :web_user_id, { name: 'WebUser_ID' },
31
- :web_password_id, { name: 'WebPassword_ID' } # rubocop:disable Style/BracesAroundHashParameters
30
+ :web_password_id, { name: 'WebPassword_ID' },
31
+ :password_list_id, { name: 'PasswordListID' } # Note: POST only # rubocop:disable Style/BracesAroundHashParameters
32
32
 
33
- read_fields :password_id, { name: 'PasswordID' } # rubocop:disable Style/BracesAroundHashParameters
33
+ read_fields :account_type,
34
+ :password_id, { name: 'PasswordID' },
35
+ :password_list
34
36
 
35
37
  # Things that can be set in a POST/PUT request
36
38
  # TODO: Do this properly
@@ -41,7 +43,6 @@ module Passwordstate
41
43
  :password_reset_schedule,
42
44
  :add_days_to_expiry_date,
43
45
  :script_id, { name: 'ScriptID' },
44
- :password_list_id, { name: 'PasswordListID' }, # POST only
45
46
  :privileged_account_id,
46
47
  :heartbeat_enabled,
47
48
  :heartbeat_schedule,
@@ -56,17 +57,24 @@ module Passwordstate
56
57
 
57
58
  def history
58
59
  raise 'Password history only available on stored passwords' unless stored?
60
+
59
61
  Passwordstate::ResourceList.new client, PasswordHistory,
60
62
  all_path: "passwordhistory/#{password_id}",
61
63
  only: :all
62
64
  end
63
65
 
66
+ def permissions
67
+ client.require_version('>= 8.4.8449')
68
+ PasswordPermission.new(_client: client, password_id: password_id)
69
+ end
70
+
64
71
  def delete(recycle = false, query = {})
65
72
  super query.merge(move_to_recycle_bin: recycle)
66
73
  end
67
74
 
68
75
  def add_dependency(data = {})
69
76
  raise 'Password dependency creation only available for stored passwords' unless stored?
77
+
70
78
  client.request :post, 'dependencies', body: self.class.passwordstatify_hash(data.merge(password_id: password_id))
71
79
  end
72
80
 
@@ -81,6 +89,7 @@ module Passwordstate
81
89
  def self.generate(client, options = {})
82
90
  results = client.request(:get, 'generatepassword', query: options).map { |r| r['Password'] }
83
91
  return results.first if results.count == 1
92
+
84
93
  results
85
94
  end
86
95
  end
@@ -130,5 +139,13 @@ module Passwordstate
130
139
  raise 'Not applicable'
131
140
  end
132
141
  end
142
+
143
+ class PasswordPermission < Permission
144
+ api_path 'passwordpermissions'
145
+
146
+ index_field :password_id
147
+
148
+ read_fields :password_id, { name: 'PasswordID' } # rubocop:disable Style/BracesAroundHashParameters
149
+ end
133
150
  end
134
151
  end
@@ -52,11 +52,24 @@ module Passwordstate
52
52
  object_data: { password_list_id: password_list_id }
53
53
  end
54
54
 
55
+ def permissions
56
+ client.require_version('>= 8.4.8449')
57
+ PasswordListPermission.new(_client: client, password_list_id: password_list_id)
58
+ end
59
+
55
60
  def full_path(unix = false)
56
61
  [tree_path, password_list].compact.join('\\').tap do |full|
57
62
  full.tr!('\\', '/') if unix
58
63
  end
59
64
  end
60
65
  end
66
+
67
+ class PasswordListPermission < Permission
68
+ api_path 'passwordlistpermissions'
69
+
70
+ index_field :password_list_id
71
+
72
+ read_fields :password_list_id, { name: 'PasswordListID' } # rubocop:disable Style/BracesAroundHashParameters
73
+ end
61
74
  end
62
75
  end
@@ -0,0 +1,16 @@
1
+ module Passwordstate
2
+ module Resources
3
+ class Permission < Passwordstate::Resource
4
+ accessor_fields :permission
5
+
6
+ # TODO: Only one of the apply_* can be set at a time
7
+ write_fields :apply_permissions_for_user_id, { name: 'ApplyPermissionsForUserID' },
8
+ :apply_permissions_for_security_group_id, { name: 'ApplyPermissionsForSecurityGroupID' },
9
+ :apply_permissions_for_security_group_name
10
+
11
+ def get
12
+ raise 'Not applicable'
13
+ end
14
+ end
15
+ end
16
+ end
@@ -2,15 +2,15 @@ module Passwordstate
2
2
  module Resources
3
3
  class Report < Passwordstate::Resource
4
4
  REPORT_PARAMETERS = {
5
- 1 => %i[user_id],
6
- 2 => %i[user_id site_id],
7
- 3 => %i[user_id duration],
8
- 4 => %i[user_id site_id duration],
9
- 5 => %i[duration],
10
- 6 => %i[],
11
- 7 => %i[user_id site_id duration],
12
- 8 => %i[],
13
- 9 => %i[],
5
+ 1 => %i[user_id],
6
+ 2 => %i[user_id site_id],
7
+ 3 => %i[user_id duration],
8
+ 4 => %i[user_id site_id duration],
9
+ 5 => %i[duration],
10
+ 6 => %i[],
11
+ 7 => %i[user_id site_id duration],
12
+ 8 => %i[],
13
+ 9 => %i[],
14
14
  10 => %i[duration],
15
15
  11 => %i[duration],
16
16
  12 => %i[site_id],
@@ -11,7 +11,9 @@ module Net
11
11
  password: password
12
12
  }
13
13
  @ntlm_auth_information[:domain] = domain unless domain.nil?
14
- @ntlm_auth_options = {}
14
+ @ntlm_auth_options = {
15
+ ntlmv2: true
16
+ }
15
17
  @ntlm_auth_options[:workstation] = workstation unless workstation.nil?
16
18
  end
17
19
  end
@@ -31,6 +33,7 @@ class String
31
33
 
32
34
  def find_line(&_block)
33
35
  raise ArgumentError, 'No block given' unless block_given?
36
+
34
37
  each_line do |line|
35
38
  return line if yield line
36
39
  end
@@ -59,7 +62,7 @@ module Passwordstate
59
62
 
60
63
  if challenge && res.code == '401'
61
64
  type2 = Net::NTLM::Message.decode64 challenge
62
- type3 = type2.response(req.ntlm_auth_information, req.ntlm_auth_options)
65
+ type3 = type2.response(req.ntlm_auth_information, req.ntlm_auth_options.dup)
63
66
 
64
67
  req['authorization'] = 'NTLM ' + type3.encode64
65
68
  req.body_stream.rewind if req.body_stream
@@ -74,4 +77,4 @@ module Passwordstate
74
77
  end
75
78
  end
76
79
 
77
- Net::HTTP.send :prepend, Passwordstate::NetHTTPExtensions unless Net::HTTP.ancestors.include? Passwordstate::NetHTTPExtensions
80
+ Net::HTTP.prepend Passwordstate::NetHTTPExtensions unless Net::HTTP.ancestors.include? Passwordstate::NetHTTPExtensions
@@ -1,3 +1,3 @@
1
1
  module Passwordstate
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: passwordstate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexander Olofsson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-14 00:00:00.000000000 Z
11
+ date: 2019-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logging
@@ -119,6 +119,7 @@ files:
119
119
  - lib/passwordstate/resources/host.rb
120
120
  - lib/passwordstate/resources/password.rb
121
121
  - lib/passwordstate/resources/password_list.rb
122
+ - lib/passwordstate/resources/permission.rb
122
123
  - lib/passwordstate/resources/report.rb
123
124
  - lib/passwordstate/util.rb
124
125
  - lib/passwordstate/version.rb
@@ -144,8 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
144
145
  - !ruby/object:Gem::Version
145
146
  version: '0'
146
147
  requirements: []
147
- rubyforge_project:
148
- rubygems_version: 2.7.6
148
+ rubygems_version: 3.0.6
149
149
  signing_key:
150
150
  specification_version: 4
151
151
  summary: A ruby API client for interacting with a passwordstate server