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


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/jamf/api/connection/token.rb', line 144

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:



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

def base_url
  @base_url
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?



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

def creation_time
  @creation_time
end

#expiresTime (readonly) Also known as: expiration

Returns:



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

def expires
  @expires
end

#last_refreshTime (readonly)

Returns when was this token last refreshed?.

Returns:

  • (Time)

    when was this token last refreshed?



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

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?



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

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

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

#accountObject

the Jamf::Account object assciated with this token



303
304
305
306
307
308
309
310
# File 'lib/jamf/api/connection/token.rb', line 303

def 
  return @account if @account

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

  @account = Jamf::APIAccount.new resp.body
end

#expired?Boolean

Returns:

  • (Boolean)


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

def expired?
  return unless @expires

  Time.now >= @expires
end

#hostObject



201
202
203
# File 'lib/jamf/api/connection/token.rb', line 201

def host
  @base_url.host
end

#init_from_pwObject

Initialize from password



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/jamf/api/connection/token.rb', line 165

def init_from_pw
  resp = token_connection(NEW_TOKEN_RSRC).post

  if resp.success?
    parse_token_from_response resp
    @last_refresh = Time.now
  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



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/jamf/api/connection/token.rb', line 183

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

  @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



354
355
356
357
358
# File 'lib/jamf/api/connection/token.rb', line 354

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

#jamf_buildString

Returns:



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

def jamf_build
  fetch_jamf_version unless @jamf_build
  @jamf_build
end

#jamf_versionGem::Version

Returns:

  • (Gem::Version)


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

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



297
298
299
# File 'lib/jamf/api/connection/token.rb', line 297

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?



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

def next_refresh
  return unless keep_alive?

  @expires - @refresh_buffer
end

#portInteger

Returns:

  • (Integer)


207
208
209
# File 'lib/jamf/api/connection/token.rb', line 207

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



321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/jamf/api/connection/token.rb', line 321

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)


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

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?



247
248
249
250
251
252
# File 'lib/jamf/api/connection/token.rb', line 247

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



371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'lib/jamf/api/connection/token.rb', line 371

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



396
397
398
399
400
401
# File 'lib/jamf/api/connection/token.rb', line 396

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”



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

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:



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

def time_to_refresh
  return unless keep_alive?

  Jamf.humanize_secs secs_to_refresh
end

#valid?Boolean

Returns:

  • (Boolean)


281
282
283
284
285
286
287
288
289
290
# File 'lib/jamf/api/connection/token.rb', line 281

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