Class: JSS::APIConnection

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/jss/api_connection.rb,
lib/jss.rb

Overview

An API connection to the JSS.

This is a singleton class, only one can exist at a time. Its one instance is created automatically when the module loads, but it isn’t connected to anything at that time.

Use it via the API constant to call the #connect method, and the #get_rsrc, #put_rsrc, #post_rsrc, & #delete_rsrc methods, q.v. below.

To access the underlying RestClient::Resource instance, use JSS::API.cnx

Constant Summary collapse

RSRC_BASE =

The base API path in the jss URL

"JSSResource"
TEST_PATH =

A url path to load to see if there’s an API available at a host. This just loads the API resource docs page

"#{RSRC_BASE}/accounts"
TEST_CONTENT =

If the test path loads correctly from a casper server, it’ll contain this text (this is what we get when we make an unauthenticated API call.)

"<p>The request requires user authentication</p>"
HTTP_PORT =

The Default port

9006
SSL_PORT =

The SSL port

8443
XML_HEADER =

The top line of an XML doc for submitting data via API

'<?xml version="1.0" encoding="UTF-8" standalone="no"?>'
DFT_OPEN_TIMEOUT =

Default timeouts in seconds

60
DFT_TIMEOUT =
60
DFT_SSL_VERSION =

The Default SSL Version

'TLSv1'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeAPIConnection

To connect, use JSS::APIConnection.instance.connect or a shortcut, JSS::API.connect



121
122
123
# File 'lib/jss/api_connection.rb', line 121

def initialize ()
  @connected = false
end

Instance Attribute Details

#cnxRestClient::Resource (readonly)

Returns the underlying connection resource.

Returns:

  • (RestClient::Resource)

    the underlying connection resource



102
103
104
# File 'lib/jss/api_connection.rb', line 102

def cnx
  @cnx
end

#connectedBoolean (readonly) Also known as: connected?

Returns are we connected right now?.

Returns:

  • (Boolean)

    are we connected right now?



105
106
107
# File 'lib/jss/api_connection.rb', line 105

def connected
  @connected
end

#jss_userString (readonly)

Returns the username who’s connected to the JSS API.

Returns:

  • (String)

    the username who’s connected to the JSS API



99
100
101
# File 'lib/jss/api_connection.rb', line 99

def jss_user
  @jss_user
end

#serverJSS::Server (readonly)

Returns the details of the JSS to which we’re connected.

Returns:

  • (JSS::Server)

    the details of the JSS to which we’re connected.



108
109
110
# File 'lib/jss/api_connection.rb', line 108

def server
  @server
end

#server_hostString (readonly)

Returns the hostname of the JSS to which we’re connected.

Returns:

  • (String)

    the hostname of the JSS to which we’re connected.



111
112
113
# File 'lib/jss/api_connection.rb', line 111

def server_host
  @server_host
end

Instance Method Details

#connect(args = {}) ⇒ true

Connect to the JSS API.

Parameters:

  • args (Hash) (defaults to: {})

    the keyed arguments for connection.

Options Hash (args):

  • :server (String)

    the hostname of the JSS API server, required if not defined in JSS::CONFIG

  • :port (Integer)

    the port number to connect with, defaults to 8443

  • :use_ssl (Boolean)

    should the connection be made over SSL? Defaults to true.

  • :verify_cert (Boolean)

    should HTTPS SSL certificates be verified. Defaults to true. If your connection raises RestClient::SSLCertificateNotVerified, and you don’t care about the validity of the SSL cert. just set this explicitly to false.

  • :user (String)

    a JSS user who has API privs, required if not defined in JSS::CONFIG

  • :pw (String, Symbol)

    Required, the password for that user, or :prompt, or :stdin If :prompt, the user is promted on the commandline to enter the password for the :user. If :stdin#, the password is read from a line of std in represented by the digit at #, so :stdin3 reads the passwd from the third line of standard input. defaults to line 1, if no digit is supplied. see JSS.stdin

  • :open_timeout (Integer)

    the number of seconds to wait for an initial response, defaults to 60

  • :timeout (Integer)

    the number of seconds before an API call times out, defaults to 60

Returns:

  • (true)

Raises:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'lib/jss/api_connection.rb', line 158

def connect (args = {})

  # the server, if not specified, might come from a couple places.
  # see #hostname
  args[:server] ||= hostname

  # settings from config if they aren't in the args
  args[:server] ||= JSS::CONFIG.api_server_name
  args[:port] ||= JSS::CONFIG.api_server_port
  args[:user] ||= JSS::CONFIG.api_username
  args[:timeout] ||= JSS::CONFIG.api_timeout
  args[:open_timeout] ||= JSS::CONFIG.api_timeout_open
  args[:ssl_version] ||= JSS::CONFIG.api_ssl_version

  # if verify cert given was NOT in the args....
  if args[:verify_cert].nil?
    # set it from the prefs
    args[:verify_cert] = JSS::CONFIG.api_verify_cert
  end

  # settings from client jamf plist if needed
  args[:port] ||=  JSS::Client.jss_port

  # default settings if needed
  args[:port] ||= SSL_PORT
  args[:timeout] ||= DFT_TIMEOUT
  args[:open_timeout] ||= DFT_OPEN_TIMEOUT

  # As of Casper 9.61 we can't use SSL, must use TLS, since SSLv3 was susceptible to poodles.
  # NOTE - this requires rest-client v 1.7.0 or higher
  # which requires mime-types 2.0 or higher, which requires ruby 1.9.2 or higher!
  # That means that support for ruby 1.8.7 stops with Casper 9.6
  args[:ssl_version] ||= DFT_SSL_VERSION


  # must have server, user, and pw
  raise JSS::MissingDataError, "No JSS :server specified, or in configuration." unless args[:server]
  raise JSS::MissingDataError, "No JSS :user specified, or in configuration." unless args[:user]
  raise JSS::MissingDataError, "Missing :pw for user '#{args[:user]}'" unless args[:pw]

  # we're using ssl if 1) args[:use_ssl] is anything but false
  # or 2) the port is the default casper ssl port.
  args[:use_ssl] = (not args[:use_ssl] == false) or (args[:port] == SSL_PORT)

  # and here's the URL
  ssl = args[:use_ssl] ? "s" : ''
  @rest_url = URI::encode "http#{ssl}://#{args[:server]}:#{args[:port]}/#{RSRC_BASE}"


  # prep the args for passing to RestClient::Resource
  # if verify_cert is anything but false, we will verify
  args[:verify_ssl] =  (args[:verify_cert] == false) ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER

  args[:password] = if args[:pw] == :prompt
    JSS.prompt_for_password "Enter the password for JSS user #{args[:user]}@#{args[:server]}:"
  elsif args[:pw].is_a?(Symbol) and args[:pw].to_s.start_with?('stdin')
    args[:pw].to_s =~ /^stdin(\d+)$/
    line = $1
    line ||= 1
    JSS.stdin line
  else
    args[:pw]
  end

  # heres our connection
  @cnx = RestClient::Resource.new("#{@rest_url}", args)

  @jss_user = args[:user]
  @server_host = args[:server]
  @connected = true
  @server = JSS::Server.new

  if @server.version < JSS.parse_jss_version(JSS::MINIMUM_SERVER_VERSION)[:version]
    raise JSS::UnsupportedError, "Your JSS Server version, #{@server.raw_version}, is to low. Must be #{JSS::MINIMUM_SERVER_VERSION} or higher."
  end

  return @connected ? @server_host : nil
end

#delete_rsrc(rsrc) ⇒ String

Delete a resource from the JSS

Parameters:

  • rsrc (String)

    the resource to create, the URL part after ‘JSSResource/’

Returns:

  • (String)

    the xml response from the server.

Raises:



352
353
354
355
356
357
358
359
# File 'lib/jss/api_connection.rb', line 352

def delete_rsrc(rsrc)
  raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
  raise MissingDataError, "Missing :rsrc" if rsrc.nil?

  ### delete the resource
  @cnx[rsrc].delete

end

#disconnectvoid

This method returns an undefined value.

With a REST connection, there isn’t any real “connection” to disconnect from So to disconnect, we just unset all our credentials.



274
275
276
277
278
279
280
# File 'lib/jss/api_connection.rb', line 274

def disconnect
  @jss_user = nil
  @rest_url = nil
  @server_host = nil
  @cnx = nil
  @connected = false
end

#get_rsrc(rsrc, format = :json) ⇒ Hash, String

Get an arbitrary JSS resource

The first argument is the resource to get (the part of the API url after the ‘JSSResource/’ )

By default we get the data in JSON, and parse it into a ruby data structure (arrays, hashes, strings, etc) with symbolized Hash keys.

Parameters:

  • rsrc (String)

    the resource to get (the part of the API url after the ‘JSSResource/’ )

  • format (Symbol) (defaults to: :json)

    either ;json or :xml If the second argument is :xml, the XML data is returned as a String.

Returns:

Raises:



300
301
302
303
304
305
306
# File 'lib/jss/api_connection.rb', line 300

def get_rsrc (rsrc, format = :json)
  raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected
  rsrc = URI::encode rsrc
  data = @cnx[rsrc].get(:accept => format)
  return JSON.parse(data, :symbolize_names => true) if format == :json
  data
end

#hostnameString

The server to which we are connected, or will try connecting to if none is specified with the call to #connect

Returns:

  • (String)

    the hostname of the server



402
403
404
405
406
407
# File 'lib/jss/api_connection.rb', line 402

def hostname
  return @server_host if @server_host
  srvr = JSS::CONFIG.api_server_name
  srvr ||= JSS::Client.jss_server
  return srvr
end

#open_timeout=(timeout) ⇒ void

This method returns an undefined value.

Reset the open-connection timeout for the rest connection

Parameters:

  • timeout (Integer)

    the new timeout in seconds



263
264
265
# File 'lib/jss/api_connection.rb', line 263

def open_timeout= (timeout)
  @cnx.options[:open_timeout] = timeout
end

#post_rsrc(rsrc, xml) ⇒ String

Create a new JSS resource

Parameters:

  • rsrc (String)

    the API resource being created, the URL part after ‘JSSResource/’

  • xml (String)

    the xml specifying the new object.

Returns:

  • (String)

    the xml response from the server.

Raises:



336
337
338
339
340
341
342
343
344
# File 'lib/jss/api_connection.rb', line 336

def post_rsrc(rsrc,xml)
  raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected

  ### convert CRs & to &#13;
  xml.gsub!(/\r/, '&#13;')

  ### send the data
  @cnx[rsrc].post xml, :content_type => 'text/xml', :accept => :json
end

#put_rsrc(rsrc, xml) ⇒ String

Change an existing JSS resource

Parameters:

  • rsrc (String)

    the API resource being changed, the URL part after ‘JSSResource/’

  • xml (String)

    the xml specifying the changes.

Returns:

  • (String)

    the xml response from the server.

Raises:



317
318
319
320
321
322
323
324
325
# File 'lib/jss/api_connection.rb', line 317

def put_rsrc(rsrc,xml)
  raise JSS::InvalidConnectionError, "Not Connected. Use JSS::API.connect first." unless @connected

  ### convert CRs & to &#13;
  xml.gsub!(/\r/, '&#13;')

  ### send the data
  @cnx[rsrc].put(xml, :content_type => 'text/xml')
end

#timeout=(timeout) ⇒ void

This method returns an undefined value.

Reset the response timeout for the rest connection

Parameters:

  • timeout (Integer)

    the new timeout in seconds



252
253
254
# File 'lib/jss/api_connection.rb', line 252

def timeout= (timeout)
  @cnx.options[:timeout] = timeout
end

#to_sString

A useful string about this connection

Returns:



241
242
243
# File 'lib/jss/api_connection.rb', line 241

def to_s
  @connected ? "Using #{@rest_url} as user #{@jss_user}" : "not connected"
end

#valid_server?(server, port = SSL_PORT) ⇒ Boolean

Test that a given hostname & port is a JSS API server

Parameters:

  • server (String)

    The hostname to test,

  • port (Integer) (defaults to: SSL_PORT)

    The port to try connecting on

Returns:

  • (Boolean)

    does the server host a JSS API?



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/jss/api_connection.rb', line 370

def valid_server? (server, port = SSL_PORT)
  # cheating by shelling out to curl, because getting open-uri, or even net/http to use
  # ssl_options like :OP_NO_SSLv2 and :OP_NO_SSLv3 will take time to figure out..
  return true if `/usr/bin/curl -s 'https://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
  return true if `/usr/bin/curl -s 'http://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
  return false

  # try ssl first
  # NOTE:  doesn't work if we can't disallow SSLv3 or force TLSv1
  # See cheat above.
  begin
    return true if open("https://#{server}:#{port}/#{TEST_PATH}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read.include? TEST_CONTENT

  rescue
    # then regular http
    begin
      return true if open("http://#{server}:#{port}/#{TEST_PATH}").read.include? TEST_CONTENT
    rescue
      # any errors = no API
      return false
    end # begin
  end #begin
  # if we're here, no API
  return false
end