Module: D3::Admin::Auth

Extended by:
Auth
Included in:
Auth
Defined in:
lib/d3/admin/auth.rb

Overview

This module contains methods for dealing with d3 admin authentication getting, & storing the passwords needed to connect to the JSS and the database as a d3 admin.

Constant Summary collapse

RW_CREDENTIAL_KINDS =

Module Constants #################

[:jss, :db, :dist]
KEYCHAIN_SERVICE_BASE =
"d3admin"
KEYCHAIN_LABEL_BASE =
"com.pixar.d3.admin"
KEYCHAIN_JSS_SERVICE =
KEYCHAIN_SERVICE_BASE + ".jss-api"
KEYCHAIN_JSS_LABEL =
KEYCHAIN_LABEL_BASE + ".jss-api"
KEYCHAIN_DB_SERVICE =
KEYCHAIN_SERVICE_BASE + ".db"
KEYCHAIN_DB_LABEL =
KEYCHAIN_LABEL_BASE + ".db"
KEYCHAIN_DIST_ACCT =
"defined_in_jss"
KEYCHAIN_DIST_SERVICE =
KEYCHAIN_SERVICE_BASE + ".distribution"
KEYCHAIN_DIST_LABEL =
KEYCHAIN_LABEL_BASE + ".distribution"
@@connected =
false

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ask_for_rw_credentials(kind) ⇒ Hash{Symbol => String}

Prompt for read-write credentials & store them in the default (login) keychain

Raises an exception after 3 failures

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

Returns:

  • (Hash{Symbol => String})

    A Hash with :user and :password values.



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/d3/admin/auth.rb', line 239

def ask_for_rw_credentials(kind)

  #$stdin.reopen("/dev/tty")

  # make sure we have a server, which will be stored in the user-level JSS::Configuration
  get_server (kind) unless kind == :dist

  # make sure the keychain is unlocked
  unlock_keychain

  thing_to_access = case kind
  when :jss then "the JSS API on #{JSS::API.hostname}"
  when :db then "the JSS MySQL db at #{JSS::DB_CNX.hostname}"
  when :dist then "read-write access to the JSS Master Distribution Point"
  else raise JSS::InvalidDataError, "argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind

  # three tries
  begin
    pw = nil
    tries = 0
    while tries != 3

      # for dist we only need a password
      if kind == :dist
        user = KEYCHAIN_DIST_ACCT
        user_text = ''
      else
        print "Username for RW access to #{thing_to_access}: "
        user = $stdin.gets.chomp
        user_text = "#{user} @ "
      end

      print "Password for #{user_text}#{thing_to_access}: "
      system "stty -echo"
      pw = $stdin.gets.chomp
      system "stty echo"
      break if check_credentials(kind, user, pw)

      puts "\nSorry, that was incorrect"
      tries += 1
    end # while

    # did we get it in 3 tries?
    raise JSS::InvalidDataError, "Three wrong attempts, please contact a Jamf Pro administrator." if 3 == tries

    save_credentials(kind, user, pw)
    puts "\nThank you, the credentials have been saved in your OS X login keychain"
  ensure
    # make sure terminal is usable at the end of this
    system "stty echo"
    puts ""
  end # begin

  # we should now have user and pw
  return {:user => user, :password =>  pw}
end

.check_credentials(kind, user = "", pw = "") ⇒ Boolean

Check a user and password for validity

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • user (String) (defaults to: "")

    the username to check

  • pw (String) (defaults to: "")

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



307
308
309
310
311
312
313
314
# File 'lib/d3/admin/auth.rb', line 307

def check_credentials(kind, user = "", pw = "")
  case kind
  when :jss then check_jss_credentials(user,pw)
  when :db then check_db_credentials(user,pw)
  when :dist then check_dist_credentials(pw)
  else raise JSS::InvalidDataError, "First argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind
end

.check_db_credentials(user, pw) ⇒ Boolean

Check a username and passwd for rw access to the JSS MySQL DB

Note: this only checks for connectivity, not permissions once connected.

Parameters:

  • user (String)

    the username to check

  • pw (String)

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



347
348
349
350
351
352
353
354
355
356
# File 'lib/d3/admin/auth.rb', line 347

def check_db_credentials(user,pw)
  begin
    JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
    JSS::DB_CNX.connect :user => user, :pw => pw, :server => JSS::CONFIG.db_server_name
  rescue Mysql::ServerError::AccessDeniedError
    return false
  end
  JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
  return true
end

.check_dist_credentials(pw) ⇒ Boolean

Check a passwd for rw access to the master distribution point in the JSS

Parameters:

  • pw (String)

    the password to try

Returns:

  • (Boolean)

    was the password valid?



364
365
366
367
368
369
# File 'lib/d3/admin/auth.rb', line 364

def check_dist_credentials(pw)
  D3::Admin::Auth.connect
  ok = JSS::DistributionPoint.master_distribution_point.check_pw :rw, pw
  D3::Admin::Auth.disconnect
  ok
end

.check_jss_credentials(user, pw) ⇒ Boolean

Check a username and passwd for rw access to the JSS API

Note: this only checks for connectivity, not permissions once connected.

Parameters:

  • user (String)

    the username to check

  • pw (String)

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



326
327
328
329
330
331
332
333
334
335
# File 'lib/d3/admin/auth.rb', line 326

def check_jss_credentials(user,pw)
  begin
    JSS::API.disconnect  if JSS::API.connected?
    JSS::API.connect :user => user, :pw => pw, :server => JSS::CONFIG.api_server_name
  rescue JSS::AuthenticationError
    return false
  end
  JSS::API.disconnect  if JSS::API.connected?
  return true
end

.connect(alt_db = false) ⇒ String

Connect to the JSS API and MySQL DB with admin credentials from the keychain

Returns:

  • (String)

    the JSS hostname to which the connection was made



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/d3/admin/auth.rb', line 61

def connect (alt_db = false)
  api = rw_credentials :jss
  db = rw_credentials :db

  JSS::DB_CNX.connect :user => db[:user], :pw => db[:password], :connect_timeout => 10
  JSS::API.connect :user => api[:user], :pw => api[:password], :open_timeout => 10
  D3::Database.check_schema_version

  @@connected = true

  return JSS::API.cnx.options[:server]
end

.connected?Boolean

Are we currently connected as an admin?

return [Boolean]

Returns:

  • (Boolean)


88
89
90
# File 'lib/d3/admin/auth.rb', line 88

def connected?
  @@connected
end

.disconnectvoid

This method returns an undefined value.

Disconnect admin credentials from the JSS API and MySQL DB



78
79
80
81
82
# File 'lib/d3/admin/auth.rb', line 78

def disconnect
  JSS::API.disconnect if JSS::API.connected?
  JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
  @@connected = false
end

.get_server(type) ⇒ void

This method returns an undefined value.

Prompt for a JSS or MySQL server hostname & port. Test that its a valid server, and save it to the User-level JSS config file

Parameters:

  • type (Symbol)

    either :jss or :db



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
# File 'lib/d3/admin/auth.rb', line 140

def get_server (type)

  case type
  when :jss
    thing = "API"
    current = JSS::CONFIG.api_server_name
    current_port = JSS::CONFIG.api_server_port
    current_port ||= JSS::APIConnection::SSL_PORT
  when :db
    thing = "MySQL DB"
    current = JSS::CONFIG.db_server_name
    current_port = JSS::CONFIG.db_server_port
    current_port ||= JSS::DBConnection::DFT_PORT
  else
    raise JSS::InvalidDataError, "Argument must be :jss or :db"
  end

  got_it = false
  until got_it
    puts
    puts "Enter the server hostname for the JSS #{thing}"
    puts "Hit return for #{current}" if current
    print "JSS #{thing} Server: "
    server_entered = $stdin.gets.chomp
    server_entered = current if server_entered.empty?

    next if server_entered.empty?

    puts
    puts "Enter the port number for the JSS #{thing} on #{server_entered}"
    puts "Hit return for #{current_port}" if current_port
    print "JSS #{thing} port: "
    port_entered = $stdin.gets.chomp
    port_entered = current_port if port_entered.empty?

    got_it = test_server_available(type, server_entered, port_entered)

    if got_it
      case type
      when :jss then
        JSS::CONFIG.api_server_name = server_entered
        JSS::CONFIG.api_server_port = port_entered
      when :db
        JSS::CONFIG.db_server_name = server_entered
        JSS::CONFIG.db_server_port = port_entered
      end # case
      JSS::CONFIG.save :user
    end # if got_it
  end # until
  return server_entered
end

.rw_credentials(kind, checking_for_existence = false) ⇒ Hash{Symbol => String}

Fetch read-write credentials from the login keychain

If the login keychain is locked, the user will be prompted to unlock it in the GUI.

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • checking_for_existence (Boolean) (defaults to: false)

    Are we just checking to see if this value has been set? If so, and it hasn’t, don’t prompt for saving, just return an empty hash.

Returns:

  • (Hash{Symbol => String})

    A Hash with :user and :password values. or empty if unset and checking for existence.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/d3/admin/auth.rb', line 107

def rw_credentials(kind, checking_for_existence = false)
  Keychain.user_interaction_allowed = true
  unlock_keychain
  search_conditions = case kind
  when :jss
    {:service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL}
  when :db
    {:service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL}
  when :dist
    {:service => KEYCHAIN_DIST_SERVICE, :account => KEYCHAIN_DIST_ACCT, :label => KEYCHAIN_DIST_LABEL}
  else
    raise JSS::InvalidDataError, "argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end #pw_item = case kind

  pw_item = Keychain.default.generic_passwords.where(search_conditions).first
  return {} if pw_item.nil? and checking_for_existence

  if pw_item
    return {:user => pw_item., :password =>  pw_item.password}
  else
    # doesn't exist in the keychain, so get from the user and save in the keychain
    ask_for_rw_credentials(kind)
  end # if pw_item
end

.save_credentials(kind, user = "", pw = "") ⇒ Boolean

Save a user and password to the login keychain

Note: assumes the validity of the credentials

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • user (String) (defaults to: "")

    the username to check

  • pw (String) (defaults to: "")

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



383
384
385
386
387
388
389
390
# File 'lib/d3/admin/auth.rb', line 383

def save_credentials(kind, user = "", pw = "")
  case kind
  when :jss then save_jss_rw_credentials(user,pw)
  when :db then save_db_rw_credentials(user,pw)
  when :dist then save_dist_rw_credentials(pw)
  else raise JSS::InvalidDataError, "First argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind
end

.save_db_rw_credentials(user, pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS DB in the login keychain

Note: assumes the validity of the user and passwd. See #check_db_pw

Parameters:

  • user (String)

    the username to save

  • pw (String)

    the password to save with the username



418
419
420
421
422
# File 'lib/d3/admin/auth.rb', line 418

def save_db_rw_credentials(user, pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL, :account => user).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL, :account => user, :password => pw
end

.save_dist_rw_credentials(pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS master dist. point in the login keychain

Note: assumes the validity of the passwd. See #check_dist_rw_pw

Parameters:

  • pw (String)

    the password to save with the username



433
434
435
436
437
# File 'lib/d3/admin/auth.rb', line 433

def save_dist_rw_credentials(pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_DIST_SERVICE, :label => KEYCHAIN_DIST_LABEL, :account => KEYCHAIN_DIST_ACCT).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_DIST_SERVICE, :label => KEYCHAIN_DIST_LABEL, :account => KEYCHAIN_DIST_ACCT, :password => pw
end

.save_jss_rw_credentials(user, pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS API in the login keychain

Note: assumes the validity of the user and passwd. See #check_jss_pw

Parameters:

  • user (String)

    the username to save

  • pw (String)

    the password to save with the username



402
403
404
405
406
# File 'lib/d3/admin/auth.rb', line 402

def save_jss_rw_credentials(user, pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL, :account => user).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL, :account => user, :password => pw
end

.test_server_available(type, server, port) ⇒ Boolean

Test that a given hostname is actually a server of the given type by testing the connection without actually logging in. Displays the connection error if unable to connect.

Parameters:

  • type (Symbol)

    either :jss or :db

  • server (String)

    the hostname to try connecting to

Returns:

  • (Boolean)

    does the host run a server of that type?



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
# File 'lib/d3/admin/auth.rb', line 202

def test_server_available (type, server, port)
  puts "Checking connection to #{server}"
  case type
  when :jss then
    if JSS::API.valid_server? server
      JSS::CONFIG.api_server_name = server
      JSS::CONFIG.save :user
      return true
    else
      failure = "'#{server}' does not host a JSS API server"
    end # if

  when :db
    if JSS::DB_CNX.valid_server? server, port
      JSS::CONFIG.db_server_name = server
      JSS::CONFIG.save :user
      return true
    else
      failure = "'#{server}' does not host a MySQL server"
    end # if

  else
    failure = "Unknown Server Type: #{type}"
  end # case

  puts "Sorry, that server is invalid: #{failure}"
  return false
end

.unlock_keychainvoid

This method returns an undefined value.

Prompt the user to unlock the default keychain

Raises:

  • (JSS::AuthenticationError)


443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/d3/admin/auth.rb', line 443

def unlock_keychain
  return true unless Keychain.default.locked?
  begin
    unlocked = false
    tries = 0
    until unlocked or tries == 3
      puts "Please enter the password for your default keychain"
      puts "(#{Keychain.default.path})"
      print "Keychain password: "
      system "stty -echo"
      pw = $stdin.gets.chomp
      system "stty echo"
      begin
        Keychain.default.unlock! pw
        unlocked = true
      rescue Keychain::AuthFailedError
        puts
        puts "Sorry that was incorrect"
        tries += 1
      end # begin..rescue
    end # until
  ensure
    system "stty echo"
  end #begin..ensure

  raise JSS::AuthenticationError, "Three incorrect attempts to unlock keychain" if tries == 3
  return true
end

Instance Method Details

#ask_for_rw_credentials(kind) ⇒ Hash{Symbol => String}

Prompt for read-write credentials & store them in the default (login) keychain

Raises an exception after 3 failures

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

Returns:

  • (Hash{Symbol => String})

    A Hash with :user and :password values.



239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/d3/admin/auth.rb', line 239

def ask_for_rw_credentials(kind)

  #$stdin.reopen("/dev/tty")

  # make sure we have a server, which will be stored in the user-level JSS::Configuration
  get_server (kind) unless kind == :dist

  # make sure the keychain is unlocked
  unlock_keychain

  thing_to_access = case kind
  when :jss then "the JSS API on #{JSS::API.hostname}"
  when :db then "the JSS MySQL db at #{JSS::DB_CNX.hostname}"
  when :dist then "read-write access to the JSS Master Distribution Point"
  else raise JSS::InvalidDataError, "argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind

  # three tries
  begin
    pw = nil
    tries = 0
    while tries != 3

      # for dist we only need a password
      if kind == :dist
        user = KEYCHAIN_DIST_ACCT
        user_text = ''
      else
        print "Username for RW access to #{thing_to_access}: "
        user = $stdin.gets.chomp
        user_text = "#{user} @ "
      end

      print "Password for #{user_text}#{thing_to_access}: "
      system "stty -echo"
      pw = $stdin.gets.chomp
      system "stty echo"
      break if check_credentials(kind, user, pw)

      puts "\nSorry, that was incorrect"
      tries += 1
    end # while

    # did we get it in 3 tries?
    raise JSS::InvalidDataError, "Three wrong attempts, please contact a Jamf Pro administrator." if 3 == tries

    save_credentials(kind, user, pw)
    puts "\nThank you, the credentials have been saved in your OS X login keychain"
  ensure
    # make sure terminal is usable at the end of this
    system "stty echo"
    puts ""
  end # begin

  # we should now have user and pw
  return {:user => user, :password =>  pw}
end

#check_credentials(kind, user = "", pw = "") ⇒ Boolean

Check a user and password for validity

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • user (String) (defaults to: "")

    the username to check

  • pw (String) (defaults to: "")

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



307
308
309
310
311
312
313
314
# File 'lib/d3/admin/auth.rb', line 307

def check_credentials(kind, user = "", pw = "")
  case kind
  when :jss then check_jss_credentials(user,pw)
  when :db then check_db_credentials(user,pw)
  when :dist then check_dist_credentials(pw)
  else raise JSS::InvalidDataError, "First argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind
end

#check_db_credentials(user, pw) ⇒ Boolean

Check a username and passwd for rw access to the JSS MySQL DB

Note: this only checks for connectivity, not permissions once connected.

Parameters:

  • user (String)

    the username to check

  • pw (String)

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



347
348
349
350
351
352
353
354
355
356
# File 'lib/d3/admin/auth.rb', line 347

def check_db_credentials(user,pw)
  begin
    JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
    JSS::DB_CNX.connect :user => user, :pw => pw, :server => JSS::CONFIG.db_server_name
  rescue Mysql::ServerError::AccessDeniedError
    return false
  end
  JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
  return true
end

#check_dist_credentials(pw) ⇒ Boolean

Check a passwd for rw access to the master distribution point in the JSS

Parameters:

  • pw (String)

    the password to try

Returns:

  • (Boolean)

    was the password valid?



364
365
366
367
368
369
# File 'lib/d3/admin/auth.rb', line 364

def check_dist_credentials(pw)
  D3::Admin::Auth.connect
  ok = JSS::DistributionPoint.master_distribution_point.check_pw :rw, pw
  D3::Admin::Auth.disconnect
  ok
end

#check_jss_credentials(user, pw) ⇒ Boolean

Check a username and passwd for rw access to the JSS API

Note: this only checks for connectivity, not permissions once connected.

Parameters:

  • user (String)

    the username to check

  • pw (String)

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



326
327
328
329
330
331
332
333
334
335
# File 'lib/d3/admin/auth.rb', line 326

def check_jss_credentials(user,pw)
  begin
    JSS::API.disconnect  if JSS::API.connected?
    JSS::API.connect :user => user, :pw => pw, :server => JSS::CONFIG.api_server_name
  rescue JSS::AuthenticationError
    return false
  end
  JSS::API.disconnect  if JSS::API.connected?
  return true
end

#connect(alt_db = false) ⇒ String

Connect to the JSS API and MySQL DB with admin credentials from the keychain

Returns:

  • (String)

    the JSS hostname to which the connection was made



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/d3/admin/auth.rb', line 61

def connect (alt_db = false)
  api = rw_credentials :jss
  db = rw_credentials :db

  JSS::DB_CNX.connect :user => db[:user], :pw => db[:password], :connect_timeout => 10
  JSS::API.connect :user => api[:user], :pw => api[:password], :open_timeout => 10
  D3::Database.check_schema_version

  @@connected = true

  return JSS::API.cnx.options[:server]
end

#connected?Boolean

Are we currently connected as an admin?

return [Boolean]

Returns:

  • (Boolean)


88
89
90
# File 'lib/d3/admin/auth.rb', line 88

def connected?
  @@connected
end

#disconnectvoid

This method returns an undefined value.

Disconnect admin credentials from the JSS API and MySQL DB



78
79
80
81
82
# File 'lib/d3/admin/auth.rb', line 78

def disconnect
  JSS::API.disconnect if JSS::API.connected?
  JSS::DB_CNX.disconnect if JSS::DB_CNX.connected?
  @@connected = false
end

#get_server(type) ⇒ void

This method returns an undefined value.

Prompt for a JSS or MySQL server hostname & port. Test that its a valid server, and save it to the User-level JSS config file

Parameters:

  • type (Symbol)

    either :jss or :db



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
# File 'lib/d3/admin/auth.rb', line 140

def get_server (type)

  case type
  when :jss
    thing = "API"
    current = JSS::CONFIG.api_server_name
    current_port = JSS::CONFIG.api_server_port
    current_port ||= JSS::APIConnection::SSL_PORT
  when :db
    thing = "MySQL DB"
    current = JSS::CONFIG.db_server_name
    current_port = JSS::CONFIG.db_server_port
    current_port ||= JSS::DBConnection::DFT_PORT
  else
    raise JSS::InvalidDataError, "Argument must be :jss or :db"
  end

  got_it = false
  until got_it
    puts
    puts "Enter the server hostname for the JSS #{thing}"
    puts "Hit return for #{current}" if current
    print "JSS #{thing} Server: "
    server_entered = $stdin.gets.chomp
    server_entered = current if server_entered.empty?

    next if server_entered.empty?

    puts
    puts "Enter the port number for the JSS #{thing} on #{server_entered}"
    puts "Hit return for #{current_port}" if current_port
    print "JSS #{thing} port: "
    port_entered = $stdin.gets.chomp
    port_entered = current_port if port_entered.empty?

    got_it = test_server_available(type, server_entered, port_entered)

    if got_it
      case type
      when :jss then
        JSS::CONFIG.api_server_name = server_entered
        JSS::CONFIG.api_server_port = port_entered
      when :db
        JSS::CONFIG.db_server_name = server_entered
        JSS::CONFIG.db_server_port = port_entered
      end # case
      JSS::CONFIG.save :user
    end # if got_it
  end # until
  return server_entered
end

#rw_credentials(kind, checking_for_existence = false) ⇒ Hash{Symbol => String}

Fetch read-write credentials from the login keychain

If the login keychain is locked, the user will be prompted to unlock it in the GUI.

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • checking_for_existence (Boolean) (defaults to: false)

    Are we just checking to see if this value has been set? If so, and it hasn’t, don’t prompt for saving, just return an empty hash.

Returns:

  • (Hash{Symbol => String})

    A Hash with :user and :password values. or empty if unset and checking for existence.



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/d3/admin/auth.rb', line 107

def rw_credentials(kind, checking_for_existence = false)
  Keychain.user_interaction_allowed = true
  unlock_keychain
  search_conditions = case kind
  when :jss
    {:service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL}
  when :db
    {:service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL}
  when :dist
    {:service => KEYCHAIN_DIST_SERVICE, :account => KEYCHAIN_DIST_ACCT, :label => KEYCHAIN_DIST_LABEL}
  else
    raise JSS::InvalidDataError, "argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end #pw_item = case kind

  pw_item = Keychain.default.generic_passwords.where(search_conditions).first
  return {} if pw_item.nil? and checking_for_existence

  if pw_item
    return {:user => pw_item., :password =>  pw_item.password}
  else
    # doesn't exist in the keychain, so get from the user and save in the keychain
    ask_for_rw_credentials(kind)
  end # if pw_item
end

#save_credentials(kind, user = "", pw = "") ⇒ Boolean

Save a user and password to the login keychain

Note: assumes the validity of the credentials

Parameters:

  • kind (Symbol)

    which kind of credentials? :jss, :db, or :dist

  • user (String) (defaults to: "")

    the username to check

  • pw (String) (defaults to: "")

    the password to try with the username

Returns:

  • (Boolean)

    were the user and password valid?



383
384
385
386
387
388
389
390
# File 'lib/d3/admin/auth.rb', line 383

def save_credentials(kind, user = "", pw = "")
  case kind
  when :jss then save_jss_rw_credentials(user,pw)
  when :db then save_db_rw_credentials(user,pw)
  when :dist then save_dist_rw_credentials(pw)
  else raise JSS::InvalidDataError, "First argument must be one of :#{RW_CREDENTIAL_KINDS.join ', :'}"
  end # case kind
end

#save_db_rw_credentials(user, pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS DB in the login keychain

Note: assumes the validity of the user and passwd. See #check_db_pw

Parameters:

  • user (String)

    the username to save

  • pw (String)

    the password to save with the username



418
419
420
421
422
# File 'lib/d3/admin/auth.rb', line 418

def save_db_rw_credentials(user, pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL, :account => user).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_DB_SERVICE, :label => KEYCHAIN_DB_LABEL, :account => user, :password => pw
end

#save_dist_rw_credentials(pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS master dist. point in the login keychain

Note: assumes the validity of the passwd. See #check_dist_rw_pw

Parameters:

  • pw (String)

    the password to save with the username



433
434
435
436
437
# File 'lib/d3/admin/auth.rb', line 433

def save_dist_rw_credentials(pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_DIST_SERVICE, :label => KEYCHAIN_DIST_LABEL, :account => KEYCHAIN_DIST_ACCT).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_DIST_SERVICE, :label => KEYCHAIN_DIST_LABEL, :account => KEYCHAIN_DIST_ACCT, :password => pw
end

#save_jss_rw_credentials(user, pw) ⇒ void

This method returns an undefined value.

Save the credentials for read-write access to the JSS API in the login keychain

Note: assumes the validity of the user and passwd. See #check_jss_pw

Parameters:

  • user (String)

    the username to save

  • pw (String)

    the password to save with the username



402
403
404
405
406
# File 'lib/d3/admin/auth.rb', line 402

def save_jss_rw_credentials(user, pw)
  pw_item = Keychain.default.generic_passwords.where(:service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL, :account => user).first
  pw_item.delete  if pw_item
  Keychain.default.generic_passwords.create :service => KEYCHAIN_JSS_SERVICE, :label => KEYCHAIN_JSS_LABEL, :account => user, :password => pw
end

#test_server_available(type, server, port) ⇒ Boolean

Test that a given hostname is actually a server of the given type by testing the connection without actually logging in. Displays the connection error if unable to connect.

Parameters:

  • type (Symbol)

    either :jss or :db

  • server (String)

    the hostname to try connecting to

Returns:

  • (Boolean)

    does the host run a server of that type?



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
# File 'lib/d3/admin/auth.rb', line 202

def test_server_available (type, server, port)
  puts "Checking connection to #{server}"
  case type
  when :jss then
    if JSS::API.valid_server? server
      JSS::CONFIG.api_server_name = server
      JSS::CONFIG.save :user
      return true
    else
      failure = "'#{server}' does not host a JSS API server"
    end # if

  when :db
    if JSS::DB_CNX.valid_server? server, port
      JSS::CONFIG.db_server_name = server
      JSS::CONFIG.save :user
      return true
    else
      failure = "'#{server}' does not host a MySQL server"
    end # if

  else
    failure = "Unknown Server Type: #{type}"
  end # case

  puts "Sorry, that server is invalid: #{failure}"
  return false
end

#unlock_keychainvoid

This method returns an undefined value.

Prompt the user to unlock the default keychain

Raises:

  • (JSS::AuthenticationError)


443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
# File 'lib/d3/admin/auth.rb', line 443

def unlock_keychain
  return true unless Keychain.default.locked?
  begin
    unlocked = false
    tries = 0
    until unlocked or tries == 3
      puts "Please enter the password for your default keychain"
      puts "(#{Keychain.default.path})"
      print "Keychain password: "
      system "stty -echo"
      pw = $stdin.gets.chomp
      system "stty echo"
      begin
        Keychain.default.unlock! pw
        unlocked = true
      rescue Keychain::AuthFailedError
        puts
        puts "Sorry that was incorrect"
        tries += 1
      end # begin..rescue
    end # until
  ensure
    system "stty echo"
  end #begin..ensure

  raise JSS::AuthenticationError, "Three incorrect attempts to unlock keychain" if tries == 3
  return true
end