Class: Passwordstate::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/passwordstate/client.rb

Constant Summary collapse

USER_AGENT =
"RubyPasswordstate/#{Passwordstate::VERSION}"
DEFAULT_HEADERS =
{
  'accept' => 'application/json',
  'user-agent' => USER_AGENT
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url, options = {}) ⇒ Client

Returns a new instance of Client.



18
19
20
21
22
23
24
25
26
# File 'lib/passwordstate/client.rb', line 18

def initialize(url, options = {})
  @server_url = URI(url)
  @validate_certificate = true
  @headers = DEFAULT_HEADERS
  @auth_data = options.select { |k, _v| i[apikey username password].include? k }
  @api_type = options.fetch(:api_type) if options.key? :api_type
  @open_timeout = options.fetch(:open_timeout, 5)
  @timeout = options.fetch(:timeout, 15)
end

Instance Attribute Details

#api_typeObject



32
33
34
# File 'lib/passwordstate/client.rb', line 32

def api_type
  @api_type || (auth_data.key?(:apikey) ? :api : :winapi)
end

#auth_dataObject

Returns the value of attribute auth_data.



14
15
16
# File 'lib/passwordstate/client.rb', line 14

def auth_data
  @auth_data
end

#headersObject

Returns the value of attribute headers.



14
15
16
# File 'lib/passwordstate/client.rb', line 14

def headers
  @headers
end

#open_timeoutObject

Returns the value of attribute open_timeout.



15
16
17
# File 'lib/passwordstate/client.rb', line 15

def open_timeout
  @open_timeout
end

#server_urlObject

Returns the value of attribute server_url.



14
15
16
# File 'lib/passwordstate/client.rb', line 14

def server_url
  @server_url
end

#timeoutObject

Returns the value of attribute timeout.



15
16
17
# File 'lib/passwordstate/client.rb', line 15

def timeout
  @timeout
end

#validate_certificateObject

Returns the value of attribute validate_certificate.



14
15
16
# File 'lib/passwordstate/client.rb', line 14

def validate_certificate
  @validate_certificate
end

Instance Method Details

#address_bookObject



46
47
48
49
# File 'lib/passwordstate/client.rb', line 46

def address_book
  ResourceList.new Passwordstate::Resources::AddressBook,
                   client: self
end

#foldersObject



51
52
53
54
55
# File 'lib/passwordstate/client.rb', line 51

def folders
  ResourceList.new Passwordstate::Resources::Folder,
                   client: self,
                   only: i[all search post]
end

#hostsObject



57
58
59
60
61
# File 'lib/passwordstate/client.rb', line 57

def hosts
  ResourceList.new Passwordstate::Resources::Host,
                   client: self,
                   except: i[search put]
end

#loggerObject



28
29
30
# File 'lib/passwordstate/client.rb', line 28

def logger
  @logger ||= Logging.logger[self]
end

#password_listsObject



68
69
70
71
72
# File 'lib/passwordstate/client.rb', line 68

def password_lists
  ResourceList.new Passwordstate::Resources::PasswordList,
                   client: self,
                   except: i[put delete]
end

#passwordsObject



63
64
65
66
# File 'lib/passwordstate/client.rb', line 63

def passwords
  ResourceList.new Passwordstate::Resources::Password,
                   client: self
end

#pretty_print(pp) ⇒ Object



151
152
153
154
155
# File 'lib/passwordstate/client.rb', line 151

def pretty_print(pp)
  return pp.pp self if respond_to? :mocha_inspect

  pp.pp_object self
end

#pretty_print_instance_variablesObject



147
148
149
# File 'lib/passwordstate/client.rb', line 147

def pretty_print_instance_variables
  instance_variables.reject { |k| i[@auth_data @http @logger].include? k }.sort
end

#request(method, api_path, query: nil, reason: nil, **options) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/passwordstate/client.rb', line 108

def request(method, api_path, query: nil, reason: nil, **options)
  uri = URI(server_url + "/#{api_type}/" + api_path)
  uri.query = URI.encode_www_form(query) unless query.nil?
  uri.query = nil if uri.query.nil? || uri.query.empty?

  req_obj = Net::HTTP.const_get(method.to_s.capitalize.to_sym).new uri
  if options.key? :body
    req_obj.body = options.fetch(:body)
    req_obj.body = req_obj.body.to_json unless req_obj.body.is_a?(String)
    req_obj['content-type'] = 'application/json'
  end

  req_obj.ntlm_auth(auth_data[:username], auth_data[:password]) if api_type == :winapi
  headers.each { |h, v| req_obj[h] = v }
  req_obj['APIKey'] = auth_data[:apikey] if api_type == :api
  req_obj['Reason'] = reason if !reason.nil? && version?('>= 8.4.8449')

  print_http req_obj
  res_obj = http.request req_obj
  print_http res_obj

  return true if res_obj.is_a? Net::HTTPNoContent

  data = JSON.parse(res_obj.body) rescue nil
  if data
    return data if res_obj.is_a? Net::HTTPSuccess

    # data = data.first if data.is_a? Array
    # parsed = data.fetch('errors', []) if data.is_a?(Hash) && data.key?('errors')
    parsed = [data].flatten

    raise Passwordstate::HTTPError.new_by_code(res_obj.code, req_obj, res_obj, parsed || [])
  else
    return res_obj.body if res_obj.is_a?(Net::HTTPSuccess) && options.fetch(:allow_html, true)

    raise Passwordstate::HTTPError.new_by_code(res_obj.code, req_obj, res_obj, [{ 'message' => res_obj.body }])
  end
end

#require_version(compare) ⇒ Object



99
100
101
102
103
104
105
106
# File 'lib/passwordstate/client.rb', line 99

def require_version(compare)
  if version.nil?
    logger.debug 'Unable to detect Passwordstate version, assuming recent enough.'
    return true
  end

  raise "Your version of Passwordstate (#{version}) doesn't support the requested feature" unless version? compare
end

#valid?Boolean

Returns:

  • (Boolean)


74
75
76
77
78
79
# File 'lib/passwordstate/client.rb', line 74

def valid?
  version
  true
rescue StandardError
  false
end

#versionObject



81
82
83
84
85
86
87
88
# File 'lib/passwordstate/client.rb', line 81

def version
  @version ||= begin
    html = request(:get, '', allow_html: true)
    version = html.find_line { |line| line.include? '<span>V</span>' }
    version = />(\d\.\d) \(Build (.+)\)</.match(version)
    "#{version[1]}.#{version[2]}" if version
  end
end

#version?(compare) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
93
94
95
96
97
# File 'lib/passwordstate/client.rb', line 90

def version?(compare)
  if version.nil?
    logger.debug 'Unable to detect Passwordstate version, assuming recent enough.'
    return true
  end

  Gem::Dependency.new(to_s, compare).match?(to_s, version)
end