Class: JSS::APIConnection

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

Overview

Instances of this class represent an API connection to the JSS.

JSS::APIConnection objects are REST connections to JSS APIs and contain (once connected) all the data needed for communication with that API, including login credentials, URLs, and so on.

The default connection

When ruby-jss is loaded, a not-yet-connected default instance of JSS::APIConnection is created, activated, and stored internally. Before using it you must call its #connect method, passing in appropriate connection details and credentials.

Here’s how to use the default connection:

require 'ruby-jss'
JSS.api.connect server: 'server.address.edu', user: 'jss-api-user', pw: :prompt

(see #connect for all the connection options)

If you’re only going to be connecting to one server, or one at a time, using the default connection is preferred. You can call its #connect method at any time to change servers or connection credentials.

Multiple connections & the currently active connection

Sometimes you need to connect simultaneously to more than one JSS. or to the same JSS with different credentials.

While multiple connection instances can be created, only one is active at a time and all API access happens through the currently active connection. (See below for how to switch between different connections)

The currently-active connection instance is available from the ‘JSS.api` method.

Making new connection instances

New connections can be created and stored in a variable using the standard ruby ‘new’ method.

If you provide connection details when calling ‘new’, they will be passed to the #connect method immediately.

production_api = JSS::APIConnection.new(
  name: 'prod',
  server: 'prodserver.address.org',
  user: 'produser',
  pw: :prompt
)

# the new connection is now stored in the variable 'production_api'.

Switching between multiple connections

Only one connection is active at a time and the currently active one is returned when you call ‘JSS.api` or its aliases `JSS.api_connection` or `JSS.connection`

To activate another connection just pass it to the JSS.use_api method like so:

JSS.use_api production_api
# the connection we stored in 'production_api' is now active

To re-activate to the default connection, just call

JSS.use_default_connection

NOTE: The APIObject list methods (e.g. JSS::Computer.all) cache the list data from the API the first time they are used, and after that when their ‘refresh’ option is true.

Those caches are stored in the APIConnection instance through- which they were read, so they won’t be incorrect when you switch connections.

Connection Names:

As seen in the example above, you can provide a ‘name:’ parameter (a String or a Symbol) when creating a new connection. The name can be used later to identify connection objects.

If you don’t provide one, the name is ‘:disconnected’ until you connect, and then ‘user@server:port’ after connecting.

The name of the default connection is always :default

To see the name of the currently active connection, just use ‘JSS.api.name`

JSS.use_api production_api
JSS.api.name  # => 'prod'

JSS.use_default_connection
JSS.api.name  # => :default

Creating, Storing and Activating a connection in one step

Both of the above steps (creating/storing a connection, and making it active) can be performed in one step using the ‘JSS.new_api_connection` method, which creates a new APIConnection, makes it the active connection, and returns it.

 production_api2 = JSS.new_api_connection(
   name: 'prod2',
   server: 'prodserver.address.org',
   user: 'produser',
   pw: :prompt
 )

JSS.api.name  # => 'prod2'

Low-level use of APIConnection instances.

For most uses, creating, activating, and connecting APIConnection instances is all you’ll need. However to access API resources that aren’t yet implemented in other parts of ruby-jss, you can use the methods #get_rsrc, #put_rsrc, #post_rsrc, & #delete_rsrc documented below.

For even lower-level work, you can access the underlying RestClient::Resource inside the APIConnection via the connection’s #cnx attribute.

APIConnection instances also have a #server attribute which contains an instance of Server q.v., representing the JSS to which it’s connected.

Constant Summary collapse

RSRC_BASE =

The base API path in the jss URL

'JSSResource'.freeze
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".freeze
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>'.freeze
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"?>'.freeze
DFT_OPEN_TIMEOUT =

Default timeouts in seconds

60
DFT_TIMEOUT =
60
DFT_SSL_VERSION =

The Default SSL Version 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

'TLSv1'.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(args = {}) ⇒ APIConnection

If name: is provided (as a String or Symbol) that will be stored as the APIConnection’s name attribute.

For other available parameters, see #connect.

If they are provided, they will be used to establish the connection immediately.

If not, you must call #connect before accessing the API.



260
261
262
263
264
265
266
# File 'lib/jss/api_connection.rb', line 260

def initialize(args = {})
  @name = args.delete :name
  @name ||= :disconnected
  @connected = false
  @object_list_cache = {}
  connect args unless args.empty?
end

Instance Attribute Details

#cnxRestClient::Resource (readonly)

Returns the underlying connection resource.

Returns:

  • (RestClient::Resource)

    the underlying connection resource



208
209
210
# File 'lib/jss/api_connection.rb', line 208

def cnx
  @cnx
end

#connectedBoolean (readonly) Also known as: connected?

Returns are we connected right now?.

Returns:

  • (Boolean)

    are we connected right now?



211
212
213
# File 'lib/jss/api_connection.rb', line 211

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



205
206
207
# File 'lib/jss/api_connection.rb', line 205

def jss_user
  @jss_user
end

#last_http_responseRestClient::Response (readonly)

Returns The response from the most recent API call.

Returns:

  • (RestClient::Response)

    The response from the most recent API call



226
227
228
# File 'lib/jss/api_connection.rb', line 226

def last_http_response
  @last_http_response
end

#nameString, Symbol (readonly)

connection during initialization, using the name: parameter. defaults to user@hostname:port

Returns:

  • (String, Symbol)

    an arbitrary name that can be given to this



234
235
236
# File 'lib/jss/api_connection.rb', line 234

def name
  @name
end

#object_list_cacheHash (readonly)

This Hash holds the most recent API query for a list of all items in any APIObject subclass, keyed by the subclass’s RSRC_LIST_KEY. See the APIObject.all class method.

When the APIObject.all method is called without an argument, and this hash has a matching value, the value is returned, rather than requerying the API. The first time a class calls .all, or whnever refresh is not false, the API is queried and the value in this hash is updated.

Returns:



245
246
247
# File 'lib/jss/api_connection.rb', line 245

def object_list_cache
  @object_list_cache
end

#portInteger (readonly)

Returns the port used for the connection.

Returns:

  • (Integer)

    the port used for the connection



220
221
222
# File 'lib/jss/api_connection.rb', line 220

def port
  @port
end

#protocolString (readonly)

Returns the protocol being used: http or https.

Returns:

  • (String)

    the protocol being used: http or https



223
224
225
# File 'lib/jss/api_connection.rb', line 223

def protocol
  @protocol
end

#rest_urlString (readonly)

Returns The base URL to to the current REST API.

Returns:

  • (String)

    The base URL to to the current REST API



229
230
231
# File 'lib/jss/api_connection.rb', line 229

def rest_url
  @rest_url
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.



214
215
216
# File 'lib/jss/api_connection.rb', line 214

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.



217
218
219
# File 'lib/jss/api_connection.rb', line 217

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)


299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/jss/api_connection.rb', line 299

def connect(args = {})
  args = apply_connection_defaults args
  verify_basic_args args
  @jss_user = args[:user]

  @rest_url = build_rest_url args

  # figure out :verify_ssl from :verify_cert
  args[:verify_ssl] = verify_ssl args

  # figure out :password from :pw
  args[:password] = acquire_password args

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

  verify_server_version

  @name = "#{@jss_user}@#{@server_host}:#{@port}" if @name.nil? || @name == :disconnected
  @connected ? hostname : nil
end

#delete_rsrc(rsrc, xml = nil) ⇒ 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:



433
434
435
436
437
438
439
440
441
442
# File 'lib/jss/api_connection.rb', line 433

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

  # payload?
  return delete_with_payload rsrc, xml if xml

  # delete the resource
  @last_http_response = @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.



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

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:



379
380
381
382
383
384
385
# File 'lib/jss/api_connection.rb', line 379

def get_rsrc(rsrc, format = :json)
  # puts object_id
  raise JSS::InvalidConnectionError, 'Not Connected. Use JSS.api.connect first.' unless @connected
  rsrc = URI.encode rsrc
  @last_http_response = @cnx[rsrc].get(accept: format)
  return JSON.parse(@last_http_response, symbolize_names: true) if format == :json
end

#hostnameString Also known as: host

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



484
485
486
487
488
489
# File 'lib/jss/api_connection.rb', line 484

def hostname
  return @server_host if @server_host
  srvr = JSS::CONFIG.api_server_name
  srvr ||= JSS::Client.jss_server
  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



345
346
347
# File 'lib/jss/api_connection.rb', line 345

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) (defaults to: '')

    the xml specifying the new object.

Returns:

  • (String)

    the xml response from the server.



415
416
417
418
419
420
421
422
423
424
425
# File 'lib/jss/api_connection.rb', line 415

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

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

  # send the data
  @last_http_response = @cnx[rsrc].post xml, content_type: 'text/xml', accept: :json
rescue RestClient::Conflict => exception
  raise_conflict_error(exception)
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.



395
396
397
398
399
400
401
402
403
404
405
# File 'lib/jss/api_connection.rb', line 395

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

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

  # send the data
  @last_http_response = @cnx[rsrc].put(xml, content_type: 'text/xml')
rescue RestClient::Conflict => exception
  raise_conflict_error(exception)
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



335
336
337
# File 'lib/jss/api_connection.rb', line 335

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

#to_sString

A useful string about this connection

Returns:



325
326
327
# File 'lib/jss/api_connection.rb', line 325

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?



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
# File 'lib/jss/api_connection.rb', line 452

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
  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
  # false
end