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

'uapi'.freeze
MIN_JAMF_VERSION =

The API version must be this or higher

Gem::Version.new('10.23.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:



188
189
190
191
192
193
194
195
196
197
# File 'lib/jamf/api/connection.rb', line 188

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:



142
143
144
# File 'lib/jamf/api/connection.rb', line 142

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:



174
175
176
# File 'lib/jamf/api/connection.rb', line 174

def collection_cache
  @collection_cache
end

#connectedBoolean (readonly) Also known as: connected?

Returns:

  • (Boolean)


149
150
151
# File 'lib/jamf/api/connection.rb', line 149

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:



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

def ext_attr_cache
  @ext_attr_cache
end

#hostString? (readonly)

Returns:



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

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.



182
183
184
# File 'lib/jamf/api/connection.rb', line 182

def last_http_response
  @last_http_response
end

#login_timeObject (readonly)

when was this connection logged in?



156
157
158
# File 'lib/jamf/api/connection.rb', line 156

def 
  @login_time
end

#nameString?

Returns:



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

def name
  @name
end

#portInteger? (readonly)

Returns:

  • (Integer, nil)


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

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



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

def pw_fallback
  @pw_fallback
end

#rest_cnxRestClient::Resource (readonly)

Returns the underlying rest resource.

Returns:

  • (RestClient::Resource)

    the underlying rest resource



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

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:



165
166
167
# File 'lib/jamf/api/connection.rb', line 165

def singleton_cache
  @singleton_cache
end

#timeoutInteger? (readonly)

Returns:

  • (Integer, nil)


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

def timeout
  @timeout
end

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

Returns:



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

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



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

def token_refresh
  @token_refresh
end

#userString? (readonly)

Returns:



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

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



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
338
339
340
# File 'lib/jamf/api/connection.rb', line 284

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



427
428
429
430
431
432
433
434
# File 'lib/jamf/api/connection.rb', line 427

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



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

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



385
386
387
388
389
390
391
392
# File 'lib/jamf/api/connection.rb', line 385

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



511
512
513
514
515
516
517
518
519
520
521
# File 'lib/jamf/api/connection.rb', line 511

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



374
375
376
377
378
379
380
381
# File 'lib/jamf/api/connection.rb', line 374

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



501
502
503
# File 'lib/jamf/api/connection.rb', line 501

def jamf_build
  @token.jamf_build
end

#jamf_versionObject



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

def jamf_version
  @token.jamf_version
end

#keep_alive=(bool) ⇒ Object

Turn keepalive on or offs



485
486
487
# File 'lib/jamf/api/connection.rb', line 485

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

#keep_alive?Boolean

Are we keeping the connection alive?

Returns:

  • (Boolean)


456
457
458
459
460
# File 'lib/jamf/api/connection.rb', line 456

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



368
369
370
371
# File 'lib/jamf/api/connection.rb', line 368

def logout
  @token.destroy
  disconnect
end

#next_refreshJamf::Timestamp?

Returns:



463
464
465
466
467
# File 'lib/jamf/api/connection.rb', line 463

def next_refresh
  return unless keep_alive?

  @token.expires - @token_refresh
end

#patch(rsrc, data) ⇒ Object



416
417
418
419
420
421
422
423
424
425
# File 'lib/jamf/api/connection.rb', line 416

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



394
395
396
397
398
399
400
401
402
403
# File 'lib/jamf/api/connection.rb', line 394

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



529
530
531
# File 'lib/jamf/api/connection.rb', line 529

def pretty_print_instance_variables
  PP_VARS
end

#put(rsrc, data) ⇒ Object



405
406
407
408
409
410
411
412
413
414
# File 'lib/jamf/api/connection.rb', line 405

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)


470
471
472
473
474
# File 'lib/jamf/api/connection.rb', line 470

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”



477
478
479
480
481
482
# File 'lib/jamf/api/connection.rb', line 477

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:



440
441
442
443
444
445
446
447
448
# File 'lib/jamf/api/connection.rb', line 440

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