Class: Jamf::Connection

Inherits:
Object show all
Defined in:
lib/jamf/api/connection.rb,
lib/jamf/api/connection/token.rb,
lib/jamf/api/connection/api_error.rb

Overview

Changes from classic Jamf::APIconnection

- uses Faraday as the REST engine
- accepts a url with connect/initialize
- only supports https, no http
- no xml
- tokens & keep_alive
- no object class method wrappers in connection objects,
  only passing connection objects into the class methods

Defined Under Namespace

Classes: APIError, Token

Constant Summary collapse

RSRC_BASE =

The start of the path for API resources

'api'.freeze
MIN_JAMF_VERSION =

The JamfPro version must be this or higher

Gem::Version.new('10.25.0')
HTTPS_SCHEME =
'https'.freeze
HTTPS_SSL_PORT =

The https default SSL port, default for Jamf Cloud servers

443
ON_PREM_SSL_PORT =

The Jamf default SSL port, default for on-prem servers

8443
SSL_PORTS =

if either of these is specified, we’ll default to SSL

[ON_PREM_SSL_PORT, HTTPS_SSL_PORT].freeze
JAMFCLOUD_DOMAIN =

Recognize Jamf Cloud servers

'.jamfcloud.com'.freeze
JAMFCLOUD_PORT =

JamfCloud connections default to 443, not 8443

HTTPS_SSL_PORT
DFT_OPEN_TIMEOUT =

Default open-connection timeout in seconds

60
DFT_TIMEOUT =

Default response timeout in seconds

60
DFT_SSL_VERSION =

The Default SSL Version

'TLSv1_2'.freeze
DFT_TOKEN_REFRESH =

refresh token if less than this many seconds until expiration. Default is 5 minutes if not specified

300
TOKEN_REUSE_MIN_LIFE =

pre-existing tokens must have this many seconds before before they expire

60
HTTP_ACCEPT_HEADER =
'Accept'.freeze
HTTP_CONTENT_TYPE_HEADER =
'Content-Type'.freeze
MIME_JSON =
'application/json'.freeze
SLASH =
'/'.freeze
VALID_URL_REGEX =
/\A#{URI.regexp(%w[https])}\z/.freeze
NOT_CONNECTED =
'Not Connected'.freeze
PP_VARS =

Only these variables are displayed with PrettyPrint This avoids, especially, the caches, which are available as attr_readers

%i[
  @name
  @connected
  @host
  @port
  @user
  @base_url
  @ssl_options
  @open_timeout
  @timeout
  @login_time
  @keep_alive
  @token_refresh
  @pw_fallback
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(url = nil, **params) ⇒ Connection

Returns a new instance of Connection.

See Also:



185
186
187
188
189
190
191
192
193
194
# File 'lib/jamf/api/connection.rb', line 185

def initialize(url = nil, **params)
  @name = params.delete :name
  @name ||= NOT_CONNECTED

  @singleton_cache = {}
  @collection_cache = {}
  @ext_attr_cache = {}

  connect(url, params) unless params[:do_not_connect]
end

Instance Attribute Details

#base_urlString? (readonly)

Returns:



140
141
142
# File 'lib/jamf/api/connection.rb', line 140

def base_url
  @base_url
end

#collection_cacheHash (readonly)

This Hash holds the most recent API data (an Array of Hashes) for the list of all items in a CollectionResource subclass, keyed by the subclass itself.

CollectionResource.all return the appropriate data from here, if it exists,

See the CollectionResource.all class method.

Returns:



171
172
173
# File 'lib/jamf/api/connection.rb', line 171

def collection_cache
  @collection_cache
end

#connectedBoolean (readonly) Also known as: connected?

Returns:

  • (Boolean)


147
148
149
# File 'lib/jamf/api/connection.rb', line 147

def connected
  @connected
end

#ext_attr_cacheHash (readonly)

This hash holds ExtensionAttribute instances, which are used for validating values passed to Extendable.set_ext_attr.

Returns:



176
177
178
# File 'lib/jamf/api/connection.rb', line 176

def ext_attr_cache
  @ext_attr_cache
end

#hostString? (readonly)

Returns:



122
123
124
# File 'lib/jamf/api/connection.rb', line 122

def host
  @host
end

#last_http_responseFaraday::Response (readonly)

Returns The response object from the last API access.

Returns:

  • (Faraday::Response)

    The response object from the last API access.



179
180
181
# File 'lib/jamf/api/connection.rb', line 179

def last_http_response
  @last_http_response
end

#login_timeObject (readonly)

when was this connection logged in?



154
155
156
# File 'lib/jamf/api/connection.rb', line 154

def 
  @login_time
end

#nameString?

Returns:



119
120
121
# File 'lib/jamf/api/connection.rb', line 119

def name
  @name
end

#portInteger? (readonly)

Returns:

  • (Integer, nil)


125
126
127
# File 'lib/jamf/api/connection.rb', line 125

def port
  @port
end

#pw_fallbackBoolean (readonly)

with the passwd used with .connect. Defaults to true

Returns:

  • (Boolean)

    if token refresh/keepaliave fails, try to get a new one



144
145
146
# File 'lib/jamf/api/connection.rb', line 144

def pw_fallback
  @pw_fallback
end

#rest_cnxRestClient::Resource (readonly)

Returns the underlying rest resource.

Returns:

  • (RestClient::Resource)

    the underlying rest resource



151
152
153
# File 'lib/jamf/api/connection.rb', line 151

def rest_cnx
  @rest_cnx
end

#singleton_cacheHash (readonly)

This Hash holds the most recently fetched instance of a SingletonResource subclass, keyed by the subclass itself.

SingletonResource.fetch will return the instance from here, if it exists, unless the first parameter is truthy.

Returns:



162
163
164
# File 'lib/jamf/api/connection.rb', line 162

def singleton_cache
  @singleton_cache
end

#timeoutInteger? (readonly)

Returns:

  • (Integer, nil)


131
132
133
# File 'lib/jamf/api/connection.rb', line 131

def timeout
  @timeout
end

#tokenJamf::Connection::Token? (readonly)

Returns:



134
135
136
# File 'lib/jamf/api/connection.rb', line 134

def token
  @token
end

#token_refreshInteger

Returns Refresh the token this many seconds before it expires.

Returns:

  • (Integer)

    Refresh the token this many seconds before it expires



137
138
139
# File 'lib/jamf/api/connection.rb', line 137

def token_refresh
  @token_refresh
end

#userString? (readonly)

Returns:



128
129
130
# File 'lib/jamf/api/connection.rb', line 128

def user
  @user
end

Instance Method Details

#connect(url = nil, **params) ⇒ Object

Connect this Connection object to an Jamf Pro API

The first parameter may be a URL (must be https) from which the host & port will be used, and if present, the user and password E.g.

connect 'https://myuser:[email protected]:8443'

which is the same as:

connect host: 'host.domain.edu', port: 8443, user: 'myuser', pw: 'pass'

When using a URL, other parameters below may be specified, however host: and port: parameters will be ignored, since they came from the URL, as will user: and :pw, if they are present in the URL. If the URL doesn’t contain user and pw, they can be provided via the parameters, or left to default values.

### Passwords The pw: parameter also accepts the symbols :prompt, and :stdin

If :prompt, the user is promted on the commandline to enter the password for the :user.

If :stdin the password is read from the first line of stdin

If :stdinX (where X is an integer) the password is read from the Xth line of stdin.see Jamf.stdin

If omitted, and running from an interactive terminal, the user is prompted as with :prompt

### Tokens Instead of a user and password, you may specify a valid ‘token:’, either:

A Jamf::Connection::Token object, which can be extracted from an active Jamf::Connection via its #token method

or

A token string e.g. “eyJhdXR…6EKoo” from any source can also be used.

Any values available via Jamf.config will be used if they are not provided in the parameters.

Parameters:

  • host: (String)

    The API server hostname. The param ‘server:’ is a synonym

  • port: (Integer)

    The API server port. If omitted, the value from Jamf.config will be used. If no config value, defaults to 443 if the host ends with ‘jamfcloud.com’ or 8443 otherwise

  • user: (String)

    The username for the API connection

  • pw: (String, Symbol)

    The password for the user, :prompt, or :stdin

  • token: (Jamf::Connection::Token, String)

    An existing, valid token. When used, there’s no need to provide user: or pw:. NOTE if using pw_fallback:true (the default) while providing a token you will also need to provide the password for the token user in the pw: parameter, or be prompted for it. If you don’t have the pw for the token user, be sure to set pw_fallback to false.

  • token_refresh: (Integer)

    Refresh the token this many seconds before it expires. Must be >= DFT_TOKEN_REFRESH

  • open_timeout: (Integer)

    The number of seconds for initial contact with the host.

  • timeout: (Integer)

    The number of seconds for a full response from the host.

  • ssl_version: (Symbol)

    The SSL version, e.g. :TLSv1_2

  • verify_cert: (Boolean)

    Should the SSL certificate be verified? Default is true, should only be set to false if using a on-prem server with a self-signed certificate, which is rare



281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/jamf/api/connection.rb', line 281

def connect(url = nil, **params)
  # This sets all the instance vars to nil, and flushes/creates the caches
  disconnect

  # If there's a Token object in :token, this sets @token,
  # and adds host, port, user from that token
  parse_token params

  # Get host, port, user and pw from a URL, add to params if needed
  parse_url url, params

  # apply defaults from config, client, and then this class.
  apply_connection_defaults params

  # Once we're here, all params have been parsed & defaulted into the
  # params hash, so
  # make sure we have the minimum needed params for a connection
  verify_basic_params params

  # turn the params into instance vars
  parse_connect_params params

  # if no @token already, get one from from
  # either a token string or a pw
  unless @token
    if params[:token].is_a? String
      @token = token_from :token_string, params[:token]
      # get the user from the token
      @user = @toke.user
      # if @pw_fallback, the pw must be acquired, since it isn't in
      # the token
      @pw = acquire_password(params[:pw]) if @pw_fallback
    else
      pw = acquire_password(params[:pw])
      @token = token_from :pw, pw
      @pw = pw if @pw_fallback
    end
  end

  # Now get some values from our token
  @base_url = @token.base_url
  @login_time = @token.

  # and make our actual connection
  @rest_cnx = create_connection

  # make sure versions are good
  validate_jamf_version

  @connected = true

  # start keepalive if needed
  start_keep_alive if @keep_alive

  # return our string output
  to_s
end

#delete(rsrc) ⇒ Object



424
425
426
427
428
429
430
431
# File 'lib/jamf/api/connection.rb', line 424

def delete(rsrc)
  validate_connected
  resp = @rest_cnx.delete rsrc
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#disconnectObject

reset all values to nil or empty



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/jamf/api/connection.rb', line 340

def disconnect
  @connected = false
  @name = NOT_CONNECTED
  @login_time = nil

  stop_keep_alive

  @host = nil
  @port = nil
  @user = nil
  @timeout = nil
  @open_timeout = nil
  @base_url = nil
  @token = nil
  @rest_cnx = nil
  @ssl_options = {}
  @keep_alive = nil
  @token_refresh = nil
  @pw_fallback = nil
  @pw = nil

  flushcache
end

#download(rsrc) ⇒ Object

GET a rsrc without doing any JSON parsing, using a temporary Faraday connection object



382
383
384
385
386
387
388
389
# File 'lib/jamf/api/connection.rb', line 382

def download(rsrc)
  temp_cnx = create_connection(false)
  resp = temp_cnx.get rsrc
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#flushcache(klass = nil) ⇒ void

This method returns an undefined value.

Flush the collection and/or ea cache for the given class, or all cached data

Parameters:

  • klass (Class) (defaults to: nil)

    the class of cache to flush



508
509
510
511
512
513
514
515
516
517
518
# File 'lib/jamf/api/connection.rb', line 508

def flushcache(klass = nil)
  if klass
    @collection_cache.delete klass
    @singleton_cache.delete klass
    @ext_attr_cache.delete klass
  else
    @collection_cache = {}
    @singleton_cache = {}
    @ext_attr_cache = {}
  end
end

#get(rsrc) ⇒ Object

Get a resource



371
372
373
374
375
376
377
378
# File 'lib/jamf/api/connection.rb', line 371

def get(rsrc)
  validate_connected
  resp = @rest_cnx.get rsrc
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#jamf_buildObject



498
499
500
# File 'lib/jamf/api/connection.rb', line 498

def jamf_build
  @token.jamf_build
end

#jamf_versionObject



494
495
496
# File 'lib/jamf/api/connection.rb', line 494

def jamf_version
  @token.jamf_version
end

#keep_alive=(bool) ⇒ Object

Turn keepalive on or offs



482
483
484
# File 'lib/jamf/api/connection.rb', line 482

def keep_alive=(bool)
  bool ? start_keep_alive : stop_keep_alive
end

#keep_alive?Boolean

Are we keeping the connection alive?

Returns:

  • (Boolean)


453
454
455
456
457
# File 'lib/jamf/api/connection.rb', line 453

def keep_alive?
  return false unless @keep_alive_thread

  @keep_alive_thread.alive?
end

#logoutObject

Same as disconnect, but invalidates the token on the server first



365
366
367
368
# File 'lib/jamf/api/connection.rb', line 365

def logout
  @token.destroy
  disconnect
end

#next_refreshJamf::Timestamp?

Returns:



460
461
462
463
464
# File 'lib/jamf/api/connection.rb', line 460

def next_refresh
  return unless keep_alive?

  @token.expires - @token_refresh
end

#patch(rsrc, data) ⇒ Object



413
414
415
416
417
418
419
420
421
422
# File 'lib/jamf/api/connection.rb', line 413

def patch(rsrc, data)
  validate_connected
  resp = @rest_cnx.patch(rsrc) do |req|
    req.body = data
  end
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#post(rsrc, data) ⇒ Object



391
392
393
394
395
396
397
398
399
400
# File 'lib/jamf/api/connection.rb', line 391

def post(rsrc, data)
  validate_connected
  resp = @rest_cnx.post(rsrc) do |req|
    req.body = data
  end
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#pretty_print_instance_variablesArray

Remove large cached items from the instance_variables used to create pretty-print (pp) output.

Returns:

  • (Array)

    the desired instance_variables



526
527
528
# File 'lib/jamf/api/connection.rb', line 526

def pretty_print_instance_variables
  PP_VARS
end

#put(rsrc, data) ⇒ Object



402
403
404
405
406
407
408
409
410
411
# File 'lib/jamf/api/connection.rb', line 402

def put(rsrc, data)
  validate_connected
  resp = @rest_cnx.put(rsrc) do |req|
    req.body = data
  end
  @last_http_response = resp
  return resp.body if resp.success?

  raise Jamf::Connection::APIError, resp
end

#secs_to_refreshFloat?

Returns:

  • (Float, nil)


467
468
469
470
471
# File 'lib/jamf/api/connection.rb', line 467

def secs_to_refresh
  return unless keep_alive?

  next_refresh - Time.now
end

#time_to_refreshString?

Returns e.g. “1 week 6 days 23 hours 49 minutes 56 seconds”.

Returns:

  • (String, nil)

    e.g. “1 week 6 days 23 hours 49 minutes 56 seconds”



474
475
476
477
478
479
# File 'lib/jamf/api/connection.rb', line 474

def time_to_refresh
  return unless keep_alive?
  return 0 if secs_to_refresh.negative?

  Jamf.humanize_secs secs_to_refresh
end

#to_sString

A useful string about this connection

Returns:



437
438
439
440
441
442
443
444
445
# File 'lib/jamf/api/connection.rb', line 437

def to_s
  str =
    if connected?
      "#{@base_url}, Token expires: #{@token ? @token.expires : '<token missing>'}"
    else
      NOT_CONNECTED
    end
  "Jamf::Connection '#{@name == NOT_CONNECTED ? object_id : @name}': #{str}"
end