Class: Jamf::Connection::Token

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

Overview

A token used for a connection to either API

Constant Summary collapse

AUTH_RSRC_VERSION =
'v1'.freeze
AUTH_RSRC =
'auth'.freeze
NEW_TOKEN_RSRC =
"#{AUTH_RSRC_VERSION}/#{AUTH_RSRC}/token".freeze
KEEP_ALIVE_RSRC =
"#{AUTH_RSRC_VERSION}/#{AUTH_RSRC}/keep-alive".freeze
INVALIDATE_RSRC =
"#{AUTH_RSRC_VERSION}/#{AUTH_RSRC}/invalidate-token".freeze
JAMF_VERSION_RSRC_VERSION =
'v1'.freeze
JAMF_VERSION_RSRC =
"#{JAMF_VERSION_RSRC_VERSION}/jamf-pro-version".freeze
JAMF_TRYITOUT_HOST =

Recognize the tryitout server, cuz its /auth endpoint is disabled, and it needs no tokens TODO: MOVE THIS TO THE CONNECTION CLASS

"tryitout#{Jamf::Connection::JAMFCLOUD_DOMAIN}".freeze
JAMF_TRYITOUT_TOKEN_BODY =
{
  token: 'This is a fake token, tryitout.jamfcloud.com uses internal tokens',
  expires: 2_000_000_000_000
}.freeze
MIN_REFRESH_BUFFER =

Minimum seconds before expiration that the token will automatically refresh. Used as the default if :refresh is not provided in the init params

300
REFRESH_RESULTS =

Used bu the last_refresh_result method

{
  refreshed: 'Refreshed',
  refreshed_pw: 'Refresh failed, but new token created with cached pw',
  refresh_failed: 'Refresh failed, could not create new token with cached pw',
  refresh_failed_no_pw_fallback: 'Refresh failed, but pw_fallback was false',
  expired_refreshed: 'Expired, but new token created with cached pw',
  expired_failed: 'Expired, could not create new token with cached pw',
  expired_no_pw_fallback: 'Expired, but pw_fallback was false'
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**params) ⇒ Token

Returns a new instance of Token.

Parameters:

  • params (Hash)

    The data for creating and maintaining the token

Options Hash (**params):

  • :token_string (String)

    An existing valid token string. If pw_fallback is true (the default) you will also need to provide the password for the original user in the pw: parameter. If you don’t, pw_fallback will be false even if you set it to true explicitly.

  • :base_url (String, URI)

    The url for the Jamf Pro server including host and port, e.g. ‘myjss.school.edu:8443/’

  • :user (String) — default: see Connection#initialize
  • :pw (String) — default: see Connection#initialize
  • :timeout (Integer)

    The timeout for creating or refreshing the token

  • :keep_alive (Boolean) — default: see Connection#connect
  • :refresh_buffer (Integer) — default: see Connection#connect
  • :pw_fallback (Boolean) — default: see Connection#connect
  • :ssl_version (String, Symbol) — default: see Connection#connect
  • :verify_cert (Boolean) — default: see Connection#connect


150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/jamf/api/connection/token.rb', line 150

def initialize(**params)
  @valid = false
  parse_params(**params)

  if params[:token_string]
    @pw_fallback = false unless @pw
    init_from_token_string params[:token_string]

  elsif @user && @pw
    init_from_pw

  else
    raise ArgumentError, 'Must provide either user: & pw: or token:'
  end

  start_keep_alive if @keep_alive
  @creation_time = Time.now
end

Instance Attribute Details

#base_urlURI (readonly)

Returns The base API url, e.g. myjamf.jamfcloud.com/.

Returns:



93
94
95
# File 'lib/jamf/api/connection/token.rb', line 93

def base_url
  @base_url
end

#creation_http_responseFaraday::Response (readonly)

Returns The response object from instantiating a new Token object by creating a new token or validating a token string. This is not updated when refreshing a token, only when calling Token.new.

Returns:

  • (Faraday::Response)

    The response object from instantiating a new Token object by creating a new token or validating a token string. This is not updated when refreshing a token, only when calling Token.new



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

def creation_http_response
  @creation_http_response
end

#creation_timeTime (readonly) Also known as: login_time

Returns when was this Jamf::Connection::Token originally created?.

Returns:

  • (Time)

    when was this Jamf::Connection::Token originally created?



96
97
98
# File 'lib/jamf/api/connection/token.rb', line 96

def creation_time
  @creation_time
end

#expiresTime (readonly) Also known as: expiration

Returns:



103
104
105
# File 'lib/jamf/api/connection/token.rb', line 103

def expires
  @expires
end

#last_refreshTime (readonly)

Returns when was this token last refreshed?.

Returns:

  • (Time)

    when was this token last refreshed?



100
101
102
# File 'lib/jamf/api/connection/token.rb', line 100

def last_refresh
  @last_refresh
end

#pw_fallbackBoolean (readonly) Also known as: pw_fallback?

Returns Should the provided passwd be cached in memory, to be used to generate a new token, if a normal refresh fails?.

Returns:

  • (Boolean)

    Should the provided passwd be cached in memory, to be used to generate a new token, if a normal refresh fails?



113
114
115
# File 'lib/jamf/api/connection/token.rb', line 113

def pw_fallback
  @pw_fallback
end

#ssl_optionsHash (readonly)

Returns the ssl version and verify cert, to pass into faraday connections.

Returns:

  • (Hash)

    the ssl version and verify cert, to pass into faraday connections



85
86
87
# File 'lib/jamf/api/connection/token.rb', line 85

def ssl_options
  @ssl_options
end

#ssl_versionString (readonly)

Returns the SSL version being used.

Returns:

  • (String)

    the SSL version being used



78
79
80
# File 'lib/jamf/api/connection/token.rb', line 78

def ssl_version
  @ssl_version
end

#tokenString (readonly) Also known as: token_string, auth_token

Returns The token data.

Returns:



88
89
90
# File 'lib/jamf/api/connection/token.rb', line 88

def token
  @token
end

#userString (readonly)

Returns The user who generated this token.

Returns:

  • (String)

    The user who generated this token



75
76
77
# File 'lib/jamf/api/connection/token.rb', line 75

def user
  @user
end

#verify_certBoolean (readonly) Also known as: verify_cert?

Returns are we verifying SSL certs?.

Returns:

  • (Boolean)

    are we verifying SSL certs?



81
82
83
# File 'lib/jamf/api/connection/token.rb', line 81

def verify_cert
  @verify_cert
end

Instance Method Details

#accountJamf::OAPISchemas::AuthorizationV1

the Jamf account assciated with this token, which contains info about privileges and Jamf acct group memberships and Jamf Acct settings

Returns:



314
315
316
317
318
319
320
321
# File 'lib/jamf/api/connection/token.rb', line 314

def 
  return @account if @account

  resp = token_connection(AUTH_RSRC, token: @token).get
  return unless resp.success?

  @account = Jamf::OAPISchemas::AuthorizationV1.new resp.body
end

#expired?Boolean

Returns:

  • (Boolean)


235
236
237
238
239
# File 'lib/jamf/api/connection/token.rb', line 235

def expired?
  return unless @expires

  Time.now >= @expires
end

#hostObject



209
210
211
# File 'lib/jamf/api/connection/token.rb', line 209

def host
  @base_url.host
end

#init_from_pwObject

Initialize from password



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/jamf/api/connection/token.rb', line 171

def init_from_pw
  resp = token_connection(NEW_TOKEN_RSRC).post

  if resp.success?
    parse_token_from_response resp
    @last_refresh = Time.now
    @creation_http_response = resp
  elsif resp.status == 401
    raise Jamf::AuthenticationError, 'Incorrect name or password'
  else
    # TODO: better error reporting here
    raise Jamf::AuthenticationError, "An error occurred while authenticating: #{resp.body}"
  end
ensure
  @pw = nil unless @pw_fallback
end

#init_from_token_string(str) ⇒ Object

Initialize from token string



190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/jamf/api/connection/token.rb', line 190

def init_from_token_string(str)
  resp = token_connection(AUTH_RSRC, token: str).get
  raise Jamf::InvalidDataError, 'Token is not valid' unless resp.success?

  @creation_http_response = resp
  @token = str
  @user = resp.body.dig :account, :username

  # if we were given a pw for the user, and expect to use it, validate it now
  if @pw && @pw_fallback
    resp = token_connection(NEW_TOKEN_RSRC).post
    raise Jamf::AuthenticationError, "Incorrect password provided for token string (user: #{@user})" unless resp.success?
  end

  # use this token to get a fresh one with a known expiration
  refresh
end

#invalidateObject Also known as: destroy

Make this token invalid



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

def invalidate
  @valid = !token_connection(INVALIDATE_RSRC, token: @token).post.success?
  @pw = nil
  stop_keep_alive
end

#jamf_buildString

Returns:



228
229
230
231
# File 'lib/jamf/api/connection/token.rb', line 228

def jamf_build
  fetch_jamf_version unless @jamf_build
  @jamf_build
end

#jamf_versionGem::Version

Returns:

  • (Gem::Version)


221
222
223
224
# File 'lib/jamf/api/connection/token.rb', line 221

def jamf_version
  fetch_jamf_version unless @jamf_version
  @jamf_version
end

#last_refresh_resultString?

What happened the last time we tried to refresh? See REFRESH_RESULTS

Returns:

  • (String, nil)

    result or nil if never refreshed



305
306
307
# File 'lib/jamf/api/connection/token.rb', line 305

def last_refresh_result
  REFRESH_RESULTS[@last_refresh_result]
end

#next_refreshTime?

when is the next rerefresh going to happen, if we are set to keep alive?

Returns:

  • (Time, nil)

    the time of the next scheduled refresh, or nil if not keep_alive?



244
245
246
247
248
# File 'lib/jamf/api/connection/token.rb', line 244

def next_refresh
  return unless keep_alive?

  @expires - @refresh_buffer
end

#portInteger

Returns:

  • (Integer)


215
216
217
# File 'lib/jamf/api/connection/token.rb', line 215

def port
  @base_url.port
end

#refreshTime Also known as: keep_alive

Use this token to get a fresh one. If a pw is provided try to use it to get a new token if a proper refresh fails.

Parameters:

  • pw (String)

    Optional password to use if token refresh fails. Must be the correct passwd or the token’s user (obviously)

Returns:

  • (Time)

    the new expiration time



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/jamf/api/connection/token.rb', line 332

def refresh
  # already expired?
  if expired?
    # try the passwd if we have it
    return refresh_with_pw(:expired_refreshed, :expired_failed) if @pw

    # no passwd fallback? no chance!
    @last_refresh_result = :expired_no_pw_fallback
    raise Jamf::InvalidTokenError, 'Token has expired'
  end

  # Now try a normal refresh of our non-expired token
  keep_alive_token_resp = token_connection(KEEP_ALIVE_RSRC, token: @token).post

  if keep_alive_token_resp.success?
    parse_token_from_response keep_alive_token_resp
    @last_refresh_result = :refreshed
    @last_refresh = Time.now
    return expires
  end

  # if we're here, the normal refresh failed, so try the pw
  return refresh_with_pw(:refreshed_pw, :refresh_failed) if @pw

  # if we're here, no pw? no chance!
  @last_refresh_result = :refresh_failed_no_pw_fallback
  raise 'An error occurred while refreshing the token'
end

#secs_remainingFloat

Returns:

  • (Float)


273
274
275
276
277
# File 'lib/jamf/api/connection/token.rb', line 273

def secs_remaining
  return unless @expires

  @expires - Time.now
end

#secs_to_refreshFloat?

how many secs until the next refresh? will return 0 during the actual refresh process.

Returns:

  • (Float, nil)

    Seconds until the next scheduled refresh, or nil if not keep_alive?



255
256
257
258
259
260
# File 'lib/jamf/api/connection/token.rb', line 255

def secs_to_refresh
  return unless keep_alive?

  secs = next_refresh - Time.now
  secs.negative? ? 0 : secs
end

#start_keep_alivevoid

This method returns an undefined value.

creates a thread that loops forever, sleeping most of the time, but waking up every 60 seconds to see if the token is expiring in the next @refresh_buffer seconds.

If so, the token is refreshed, and we keep looping and sleeping.

Sets @keep_alive_thread to the Thread object



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/jamf/api/connection/token.rb', line 381

def start_keep_alive
  return if @keep_alive_thread
  raise 'Token expired, cannot refresh' if expired?

  @keep_alive_thread =
    Thread.new do
      loop do
        sleep 60
        begin
          next if secs_remaining > @refresh_buffer

          refresh
        rescue
          # TODO: Some kind of error reporting
          next
        end
      end # loop
    end # thread
end

#stop_keep_alivevoid

This method returns an undefined value.

Kills the @keep_alive_thread, if it exists, and sets



406
407
408
409
410
411
# File 'lib/jamf/api/connection/token.rb', line 406

def stop_keep_alive
  return unless @keep_alive_thread

  @keep_alive_thread.kill if @keep_alive_thread.alive?
  @keep_alive_thread = nil
end

#time_remainingString

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

Returns:

  • (String)

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



281
282
283
284
285
# File 'lib/jamf/api/connection/token.rb', line 281

def time_remaining
  return unless @expires

  JSS.humanize_secs secs_remaining
end

#time_to_refreshString?

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

Returns:



265
266
267
268
269
# File 'lib/jamf/api/connection/token.rb', line 265

def time_to_refresh
  return unless keep_alive?

  Jamf.humanize_secs secs_to_refresh
end

#valid?Boolean

Returns:

  • (Boolean)


289
290
291
292
293
294
295
296
297
298
# File 'lib/jamf/api/connection/token.rb', line 289

def valid?
  @valid =
    if expired?
      false
    elsif !@token
      false
    else
      token_connection(AUTH_RSRC, token: @token).get.success?
    end
end