passwordstate 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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