Class: JSS::APIConnection

Inherits:
Object
  • Object
show all
Includes:
Singleton
Defined in:
lib/jss-api/api_connection.rb,
lib/jss-api.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

"api"
TEST_CONTENT =

If the test path loads correctly from a casper server, it’ll contain this text

"<title>JSS REST API Resource Documentation</title>"
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



119
120
121
# File 'lib/jss-api/api_connection.rb', line 119

def initialize ()
  @connected = false
end

Instance Attribute Details

#cnxRestClient::Resource (readonly)

Returns the underlying connection resource.

Returns:

  • (RestClient::Resource)

    the underlying connection resource



100
101
102
# File 'lib/jss-api/api_connection.rb', line 100

def cnx
  @cnx
end

#connectedBoolean (readonly) Also known as: connected?

Returns are we connected right now?.

Returns:

  • (Boolean)

    are we connected right now?



103
104
105
# File 'lib/jss-api/api_connection.rb', line 103

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



97
98
99
# File 'lib/jss-api/api_connection.rb', line 97

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.



106
107
108
# File 'lib/jss-api/api_connection.rb', line 106

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.



109
110
111
# File 'lib/jss-api/api_connection.rb', line 109

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:



156
157
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
# File 'lib/jss-api/api_connection.rb', line 156

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:



342
343
344
345
346
347
348
349
# File 'lib/jss-api/api_connection.rb', line 342

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.



264
265
266
267
268
269
270
# File 'lib/jss-api/api_connection.rb', line 264

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:



290
291
292
293
294
295
296
# File 'lib/jss-api/api_connection.rb', line 290

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



383
384
385
386
387
388
# File 'lib/jss-api/api_connection.rb', line 383

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



253
254
255
# File 'lib/jss-api/api_connection.rb', line 253

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:



326
327
328
329
330
331
332
333
334
# File 'lib/jss-api/api_connection.rb', line 326

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:



307
308
309
310
311
312
313
314
315
# File 'lib/jss-api/api_connection.rb', line 307

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



242
243
244
# File 'lib/jss-api/api_connection.rb', line 242

def timeout= (timeout)
  @cnx.options[:timeout] = timeout
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?



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'lib/jss-api/api_connection.rb', line 360

def valid_server? (server, port = SSL_PORT)
  # try ssl first
  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