ruby-jss 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +7 -0
  3. data/CHANGES.md +112 -0
  4. data/LICENSE.txt +174 -0
  5. data/README.md +426 -0
  6. data/THANKS.md +6 -0
  7. data/bin/cgrouper +485 -0
  8. data/bin/subnet-update +400 -0
  9. data/lib/jss-api.rb +2 -0
  10. data/lib/jss.rb +190 -0
  11. data/lib/jss/api_connection.rb +410 -0
  12. data/lib/jss/api_object.rb +616 -0
  13. data/lib/jss/api_object/advanced_search.rb +389 -0
  14. data/lib/jss/api_object/advanced_search/advanced_computer_search.rb +95 -0
  15. data/lib/jss/api_object/advanced_search/advanced_mobile_device_search.rb +96 -0
  16. data/lib/jss/api_object/advanced_search/advanced_user_search.rb +95 -0
  17. data/lib/jss/api_object/building.rb +92 -0
  18. data/lib/jss/api_object/category.rb +147 -0
  19. data/lib/jss/api_object/computer.rb +852 -0
  20. data/lib/jss/api_object/creatable.rb +98 -0
  21. data/lib/jss/api_object/criteriable.rb +189 -0
  22. data/lib/jss/api_object/criteriable/criteria.rb +231 -0
  23. data/lib/jss/api_object/criteriable/criterion.rb +228 -0
  24. data/lib/jss/api_object/department.rb +93 -0
  25. data/lib/jss/api_object/distribution_point.rb +560 -0
  26. data/lib/jss/api_object/extendable.rb +221 -0
  27. data/lib/jss/api_object/extension_attribute.rb +466 -0
  28. data/lib/jss/api_object/extension_attribute/computer_extension_attribute.rb +362 -0
  29. data/lib/jss/api_object/extension_attribute/mobile_device_extension_attribute.rb +189 -0
  30. data/lib/jss/api_object/extension_attribute/user_extension_attribute.rb +117 -0
  31. data/lib/jss/api_object/group.rb +380 -0
  32. data/lib/jss/api_object/group/computer_group.rb +124 -0
  33. data/lib/jss/api_object/group/mobile_device_group.rb +139 -0
  34. data/lib/jss/api_object/group/user_group.rb +139 -0
  35. data/lib/jss/api_object/ldap_server.rb +535 -0
  36. data/lib/jss/api_object/locatable.rb +286 -0
  37. data/lib/jss/api_object/matchable.rb +97 -0
  38. data/lib/jss/api_object/mobile_device.rb +556 -0
  39. data/lib/jss/api_object/netboot_server.rb +148 -0
  40. data/lib/jss/api_object/network_segment.rb +414 -0
  41. data/lib/jss/api_object/osx_configuration_profile.rb +262 -0
  42. data/lib/jss/api_object/package.rb +839 -0
  43. data/lib/jss/api_object/peripheral.rb +335 -0
  44. data/lib/jss/api_object/peripheral_type.rb +295 -0
  45. data/lib/jss/api_object/policy.rb +898 -0
  46. data/lib/jss/api_object/purchasable.rb +316 -0
  47. data/lib/jss/api_object/removable_macaddr.rb +98 -0
  48. data/lib/jss/api_object/scopable.rb +136 -0
  49. data/lib/jss/api_object/scopable/scope.rb +621 -0
  50. data/lib/jss/api_object/script.rb +631 -0
  51. data/lib/jss/api_object/self_servable.rb +356 -0
  52. data/lib/jss/api_object/site.rb +93 -0
  53. data/lib/jss/api_object/software_update_server.rb +109 -0
  54. data/lib/jss/api_object/updatable.rb +117 -0
  55. data/lib/jss/api_object/uploadable.rb +138 -0
  56. data/lib/jss/api_object/user.rb +272 -0
  57. data/lib/jss/client.rb +504 -0
  58. data/lib/jss/compatibility.rb +66 -0
  59. data/lib/jss/composer.rb +185 -0
  60. data/lib/jss/configuration.rb +306 -0
  61. data/lib/jss/db_connection.rb +298 -0
  62. data/lib/jss/exceptions.rb +95 -0
  63. data/lib/jss/ruby_extensions.rb +35 -0
  64. data/lib/jss/ruby_extensions/filetest.rb +43 -0
  65. data/lib/jss/ruby_extensions/hash.rb +79 -0
  66. data/lib/jss/ruby_extensions/ipaddr.rb +91 -0
  67. data/lib/jss/ruby_extensions/pathname.rb +77 -0
  68. data/lib/jss/ruby_extensions/string.rb +59 -0
  69. data/lib/jss/ruby_extensions/time.rb +63 -0
  70. data/lib/jss/server.rb +108 -0
  71. data/lib/jss/utility.rb +478 -0
  72. data/lib/jss/version.rb +31 -0
  73. metadata +187 -0
@@ -0,0 +1,504 @@
1
+ ### Copyright 2016 Pixar
2
+ ###
3
+ ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
+ ### with the following modification; you may not use this file except in
5
+ ### compliance with the Apache License and the following modification to it:
6
+ ### Section 6. Trademarks. is deleted and replaced with:
7
+ ###
8
+ ### 6. Trademarks. This License does not grant permission to use the trade
9
+ ### names, trademarks, service marks, or product names of the Licensor
10
+ ### and its affiliates, except as required to comply with Section 4(c) of
11
+ ### the License and to reproduce the content of the NOTICE file.
12
+ ###
13
+ ### You may obtain a copy of the Apache License at
14
+ ###
15
+ ### http://www.apache.org/licenses/LICENSE-2.0
16
+ ###
17
+ ### Unless required by applicable law or agreed to in writing, software
18
+ ### distributed under the Apache License with the above modification is
19
+ ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
+ ### KIND, either express or implied. See the Apache License for the specific
21
+ ### language governing permissions and limitations under the Apache License.
22
+ ###
23
+ ###
24
+
25
+ ###
26
+ module JSS
27
+
28
+ #####################################
29
+ ### Module Variables
30
+ #####################################
31
+
32
+ #####################################
33
+ ### Module Methods
34
+ #####################################
35
+
36
+ #####################################
37
+ ### Classes
38
+ #####################################
39
+
40
+ ###
41
+ ### This class represents a Casper/JSS Client computer, on which
42
+ ### this code is running.
43
+ ###
44
+ ### Since the class represents the current machine, there's no need
45
+ ### to make an instance of it, all methods are class methods.
46
+ ###
47
+ ### At the moment, only Macintosh computers are supported.
48
+ ###
49
+ ###
50
+ class Client
51
+
52
+ #####################################
53
+ ### Class Constants
54
+ #####################################
55
+
56
+ ### The Pathname to the jamf binary executable
57
+ ### As of El Capitan (OS X 10.11) the location has moved.
58
+ ORIG_JAMF_BINARY = Pathname.new "/usr/sbin/jamf"
59
+ ELCAP_JAMF_BINARY = Pathname.new "/usr/local/jamf/bin/jamf"
60
+ JAMF_BINARY = ELCAP_JAMF_BINARY.executable? ? ELCAP_JAMF_BINARY : ORIG_JAMF_BINARY
61
+
62
+ ### The Pathname to the jamfHelper executable
63
+ JAMF_HELPER = Pathname.new "/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
64
+
65
+ ### The window_type options for jamfHelper
66
+ JAMF_HELPER_WINDOW_TYPES = {
67
+ :hud => 'hud',
68
+ :utility => 'utility',
69
+ :util => 'utility',
70
+ :full_screen => 'fs',
71
+ :fs => 'fs'
72
+ }
73
+
74
+ ### The possible window positions for jamfHelper
75
+ JAMF_HELPER_WINDOW_POSITIONS = [nil, :ul, :ll, :ur, :lr]
76
+
77
+ ### The available buttons in jamfHelper
78
+ JAMF_HELPER_BUTTONS = [1,2]
79
+
80
+ ### The possible alignment positions in jamfHelper
81
+ JAMF_HELPER_ALIGNMENTS = [:right, :left, :center, :justified, :natural]
82
+
83
+ ### The Pathname to the preferences plist used by the jamf binary
84
+ JAMF_PLIST = Pathname.new "/Library/Preferences/com.jamfsoftware.jamf.plist"
85
+
86
+ ### The Pathname to the JAMF support folder
87
+ JAMF_SUPPORT_FOLDER = Pathname.new "/Library/Application Support/JAMF"
88
+
89
+ ### The JAMF receipts folder, where package installs are tracked.
90
+ RECEIPTS_FOLDER = JAMF_SUPPORT_FOLDER + "Receipts"
91
+
92
+ ### The JAMF downloads folder
93
+ DOWNLOADS_FOLDER = JAMF_SUPPORT_FOLDER + "Downloads"
94
+
95
+ ### These jamf commands don't need root privs (most do)
96
+ ROOTLESS_JAMF_COMMANDS = [
97
+ :about,
98
+ :checkJSSConnection,
99
+ :getARDFields,
100
+ :getComputerName,
101
+ :help,
102
+ :listUsers,
103
+ :version ]
104
+
105
+ #####################################
106
+ ### Class Variables
107
+ #####################################
108
+
109
+ #####################################
110
+ ### Class Methods
111
+ #####################################
112
+
113
+ ###
114
+ ### Get the current IP address as a String.
115
+ ###
116
+ ### This handy code doesn't acutally make a UDP connection,
117
+ ### it just starts to set up the connection, then uses that to get
118
+ ### the local IP.
119
+ ###
120
+ ### Lifted gratefully from
121
+ ### http://coderrr.wordpress.com/2008/05/28/get-your-local-ip-address/
122
+ ###
123
+ ### @return [String] the current IP address.
124
+ ###
125
+ def self.my_ip_address
126
+ ### turn off reverse DNS resolution temporarily
127
+ ### @note the 'socket' library has already been required by 'rest-client'
128
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
129
+
130
+ UDPSocket.open do |s|
131
+ s.connect '192.168.0.0', 1
132
+ s.addr.last
133
+ end
134
+ ensure
135
+ Socket.do_not_reverse_lookup = orig
136
+ end
137
+
138
+
139
+
140
+ ###
141
+ ### @return [Boolean] is the jamf binary installed?
142
+ ###
143
+ def self.installed?
144
+ JAMF_BINARY.executable?
145
+ end
146
+
147
+ ###
148
+ ### @return [String,nil] the version of the jamf binary installed on this client, nil if not installed
149
+ ###
150
+ def self.jamf_version
151
+ self.installed? ? self.run_jamf(:version).chomp.split('=')[1] : nil
152
+ end
153
+
154
+ ###
155
+ ### @return [String] the url to the JSS for this client
156
+ ###
157
+ def self.jss_url
158
+ @url = self.jamf_plist['jss_url']
159
+ return nil if @url.nil?
160
+ @url =~ %r{(https?)://(.+):(\d+)/}
161
+ @protocol = $1
162
+ @server = $2
163
+ @port = $3
164
+ return @url
165
+ end
166
+
167
+ ###
168
+ ### @return [String] the JSS server for this client
169
+ ###
170
+ def self.jss_server
171
+ self.jss_url
172
+ return @server
173
+ end
174
+
175
+ ###
176
+ ### @return [String] the protocol to the JSS for this client, "http" or "https"
177
+ ###
178
+ def self.jss_protocol
179
+ self.jss_url
180
+ return @protocol
181
+ end
182
+
183
+ ###
184
+ ### @return [Integer] the port to the JSS for this client
185
+ ###
186
+ def self.jss_port
187
+ self.jss_url
188
+ @port ? @port.to_i : 80
189
+ end
190
+
191
+ ###
192
+ ### @return [Hash] the parsed contents of the JAMF_PLIST if it exists, an empty hash if not
193
+ ###
194
+ def self.jamf_plist
195
+ return {} unless JAMF_PLIST.file?
196
+ Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape JSS::Client::JAMF_PLIST.to_s}`
197
+ end
198
+
199
+
200
+ ###
201
+ ### @return [Array<Pathname>] an array of Pathnames for all regular files in the jamf receipts folder
202
+ ###
203
+ def self.receipts
204
+ raise JSS::NoSuchItemError, "The JAMF Receipts folder doesn't exist on this computer." unless RECEIPTS_FOLDER.exist?
205
+ RECEIPTS_FOLDER.children.select{|c| c.file?}
206
+ end
207
+
208
+ ###
209
+ ### @return [Boolean] is the JSS available now?
210
+ ###
211
+ def self.jss_available?
212
+ output = run_jamf :checkJSSConnection, "-retry 1"
213
+ $?.exitstatus == 0
214
+ end
215
+
216
+
217
+ ###
218
+ ### @return [JSS::Computer] The JSS record for this computer
219
+ ###
220
+ def self.jss_record
221
+ begin
222
+ JSS::Computer.new :udid => self.udid
223
+ rescue JSS::NoSuchItemError
224
+ JSS::Computer.new :serial_number => self.serial_number
225
+ end
226
+ end
227
+
228
+ ###
229
+ ### @return [String] the UUID/UDID for this computer
230
+ ###
231
+ def self.udid
232
+ self.hardware_data["platform_UUID"]
233
+ end
234
+
235
+ ###
236
+ ### @return [String] the serial number for this computer
237
+ ###
238
+ def self.serial_number
239
+ self.hardware_data["serial_number"]
240
+ end
241
+
242
+ ###
243
+ ### @return [Hash] the HardwareDataType data from the system_profiler command
244
+ ###
245
+ def self.hardware_data
246
+ raw = `/usr/sbin/system_profiler SPHardwareDataType -xml 2>/dev/null`
247
+ Plist.parse_xml(raw)[0]["_items"][0]
248
+ end
249
+
250
+
251
+ ### Run an arbitrary jamf binary command.
252
+ ###
253
+ ### @note Most jamf commands require superuser/root privileges.
254
+ ###
255
+ ### @param command[String,Symbol] the jamf binary command to run
256
+ ### The command is the single jamf command that comes after the/usr/bin/jamf.
257
+ ###
258
+ ### @param args[String,Array] the arguments passed to the jamf command.
259
+ ### This is to be passed to Kernel.` (backtick), after being combined with the
260
+ ### jamf binary and the jamf command
261
+ ###
262
+ ### @param verbose[Boolean] Should the stdout & stderr of the jamf binary be sent to
263
+ ### the current stdout in realtime, as well as returned as a string?
264
+ ###
265
+ ### @return [String] the stdout & stderr of the jamf binary.
266
+ ###
267
+ ### @example
268
+ ### These two are equivalent:
269
+ ###
270
+ ### JSS::Client.run_jamf "recon", "-assetTag 12345 -department 'IT Support'"
271
+ ###
272
+ ### JSS::Client.run_jamf :recon, ['-assetTag', '12345', '-department', 'IT Support'"]
273
+ ###
274
+ ###
275
+ ### The details of the Process::Status for the jamf binary process can be captured from $?
276
+ ### immediately after calling. (See Process::Status)
277
+ ###
278
+ def self.run_jamf(command, args = nil, verbose = false)
279
+ raise JSS::UnmanagedError, "The jamf binary is not installed on this computer." unless self.installed?
280
+ raise JSS::UnsupportedError, "You must have root privileges to run that jamf binary command" unless ROOTLESS_JAMF_COMMANDS.include? command.to_sym or JSS.superuser?
281
+
282
+ cmd = case args
283
+ when nil
284
+ "#{JAMF_BINARY} #{command}"
285
+ when String
286
+ "#{JAMF_BINARY} #{command} #{args}"
287
+ when Array
288
+ "#{([JAMF_BINARY.to_s, command] + args).join(' ')}"
289
+ else
290
+ raise JSS::InvalidDataError, "args must be a String or Array of Strings"
291
+ end # case
292
+
293
+ cmd += " -verbose" if verbose and (not cmd.include? " -verbose")
294
+ puts "Running: #{cmd}" if verbose
295
+
296
+ output = []
297
+ IO.popen("#{cmd} 2>&1") do |proc|
298
+ while line = proc.gets
299
+ output << line
300
+ puts line if verbose
301
+ end
302
+ end
303
+ install_out = output.join('')
304
+ install_out.force_encoding("UTF-8") if install_out.respond_to? :force_encoding
305
+ return install_out
306
+ end # run_jamf
307
+
308
+
309
+ ### A wrapper for the jamfHelper command, which can display a window on the client machine.
310
+ ###
311
+ ### The first parameter must be a symbol defining what kind of window to display. The options are
312
+ ### - :hud - creates an Apple "Heads Up Display" style window
313
+ ### - :utility or :util - creates an Apple "Utility" style window
314
+ ### - :fs or :full_screen or :fullscreen - creates a full screen window that restricts all user input
315
+ ### WARNING: Remote access must be used to unlock machines in this mode
316
+ ###
317
+ ### The remaining options Hash can contain any of the options listed. See below for descriptions.
318
+ ###
319
+ ### The value returned is the Integer exitstatus/stdout (both are the same) of the jamfHelper command.
320
+ ### The meanings of those integers are:
321
+ ###
322
+ ### - 0 - Button 1 was clicked
323
+ ### - 1 - The Jamf Helper was unable to launch
324
+ ### - 2 - Button 2 was clicked
325
+ ### - 3 - Process was started as a launchd task
326
+ ### - XX1 - Button 1 was clicked with a value of XX seconds selected in the drop-down
327
+ ### - XX2 - Button 2 was clicked with a value of XX seconds selected in the drop-down
328
+ ### - 239 - The exit button was clicked
329
+ ### - 240 - The "ProductVersion" in sw_vers did not return 10.5.X, 10.6.X or 10.7.X
330
+ ### - 243 - The window timed-out with no buttons on the screen
331
+ ### - 250 - Bad "-windowType"
332
+ ### - 254 - Cancel button was select with delay option present
333
+ ### - 255 - No "-windowType"
334
+ ###
335
+ ### See also /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
336
+ ###
337
+ ### @note the -startlaunchd and -kill options are not available in this implementation, since
338
+ ### they don't work at the moment (casper 9.4).
339
+ ### -startlaunchd seems to be required to NOT use launchd, and when it's ommited, an error is generated
340
+ ### about the launchd plist permissions being incorrect.
341
+ ###
342
+ ### @param window_type[Symbol] The type of window to display
343
+ ###
344
+ ### @param opts[Hash] the options for the window
345
+ ###
346
+ ### @option opts :window_position [Symbol,nil] one of [ nil, :ul, :ll. :ur, :lr ]
347
+ ### Positions window in the upper right, upper left, lower right or lower left of the user's screen
348
+ ### If no input is given, the window defaults to the center of the screen
349
+ ###
350
+ ### @option opts :title [String]
351
+ ### Sets the window's title to the specified string
352
+ ###
353
+ ### @option opts :heading [String]
354
+ ### Sets the heading of the window to the specified string
355
+ ###
356
+ ### @option opts :align_heading [Symbol] one of [:right, :left, :center, :justified, :natural]
357
+ ### Aligns the heading to the specified alignment
358
+ ###
359
+ ### @option opts :description [String]
360
+ ### Sets the main contents of the window to the specified string
361
+ ###
362
+ ### @option opts :align_description [Symbol] one of [:right, :left, :center, :justified, :natural]
363
+ ### Aligns the description to the specified alignment
364
+ ###
365
+ ### @option opts :icon [String,Pathname]
366
+ ### Sets the windows image field to the image located at the specified path
367
+ ###
368
+ ### @option opts :icon_size [Integer]
369
+ ### Changes the image frame to the specified pixel size
370
+ ###
371
+ ### @option opts :full_screen_icon [any value]
372
+ ### Scales the "icon" to the full size of the window.
373
+ ### Note: Only available in full screen mode
374
+ ###
375
+ ### @option opts :button1 [String]
376
+ ### Creates a button with the specified label
377
+ ###
378
+ ### @option opts :button2 [String]
379
+ ### Creates a second button with the specified label
380
+ ###
381
+ ### @option opts :default_button [Integer] either 1 or 2
382
+ ### Sets the default button of the window to the specified button. The Default Button will respond to "return"
383
+ ###
384
+ ### @option opts :cancel_button [Integer] either 1 or 2
385
+ ### Sets the cancel button of the window to the specified button. The Cancel Button will respond to "escape"
386
+ ###
387
+ ### @option opts :timeout [Integer]
388
+ ### Causes the window to timeout after the specified amount of seconds
389
+ ### Note: The timeout will cause the default button, button 1 or button 2 to be selected (in that order)
390
+ ###
391
+ ### @option opts :show_delay_options [String,Array<Integer>] A String of comma-separated Integers, or an Array of Integers.
392
+ ### Enables the "Delay Options Mode". The window will display a dropdown with the values passed through the string
393
+ ###
394
+ ### @option opts :countdown [any value]
395
+ ### Displays a string notifying the user when the window will time out
396
+ ###
397
+ ### @option opts :align_countdown [Symbol] one of [:right, :left, :center, :justified, :natural]
398
+ ### Aligns the countdown to the specified alignment
399
+ ###
400
+ ### @option opts :lock_hud [any value]
401
+ ### Removes the ability to exit the HUD by selecting the close button
402
+ ###
403
+ ### @return [Integer] the exit status of the jamfHelper command. See above.
404
+ ###
405
+ def self.jamf_helper(window_type = :hud, opts = {})
406
+
407
+ raise JSS::UnmanagedError, "The jamfHelper app is not installed properly on this computer." unless JAMF_HELPER.executable?
408
+
409
+ unless JAMF_HELPER_WINDOW_TYPES.include? window_type
410
+ raise JSS::InvalidDataError, "The first parameter must be a window type, one of :#{JAMF_HELPER_WINDOW_TYPES.keys.join(', :')}."
411
+ end
412
+
413
+ # start building the arg array
414
+
415
+ args = ["-startlaunchd", "-windowType", JAMF_HELPER_WINDOW_TYPES[window_type]]
416
+
417
+ opts.keys.each do |opt|
418
+ case opt
419
+ when :window_position
420
+ raise JSS::InvalidDataError, ":window_position must be one of :#{JAMF_HELPER_WINDOW_POSITIONS.join(', :')}." unless JAMF_HELPER_WINDOW_POSITIONS.include? opts[opt].to_sym
421
+ args << "-windowPosition"
422
+ args << opts[opt].to_s
423
+
424
+ when :title
425
+ args << "-title"
426
+ args << opts[opt].to_s
427
+
428
+ when :heading
429
+ args << "-heading"
430
+ args << opts[opt].to_s
431
+
432
+ when :align_heading
433
+ raise JSS::InvalidDataError, ":align_heading must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
434
+ args << "-alignHeading"
435
+ args << opts[opt].to_s
436
+
437
+ when :description
438
+ args << "-description"
439
+ args << opts[opt].to_s
440
+
441
+ when :align_description
442
+ raise JSS::InvalidDataError, ":align_description must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
443
+ args << "-alignDescription"
444
+ args << opts[opt].to_s
445
+
446
+ when :icon
447
+ args << "-icon"
448
+ args << opts[opt].to_s
449
+
450
+ when :icon_size
451
+ args << "-iconSize"
452
+ args << opts[opt].to_s
453
+
454
+ when :full_screen_icon
455
+ args << "-fullScreenIcon"
456
+
457
+ when :button1
458
+ args << "-button1"
459
+ args << opts[opt].to_s
460
+
461
+ when :button2
462
+ args << "-button2"
463
+ args << opts[opt].to_s
464
+
465
+ when :default_button
466
+ raise JSS::InvalidDataError, ":default_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless JAMF_HELPER_BUTTONS.include? opts[opt]
467
+ args << "-defaultButton"
468
+ args << opts[opt].to_s
469
+
470
+ when :cancel_button
471
+ raise JSS::InvalidDataError, ":cancel_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless JAMF_HELPER_BUTTONS.include? opts[opt]
472
+ args << "-cancelButton"
473
+ args << opts[opt].to_s
474
+
475
+ when :timeout
476
+ args << "-timeout"
477
+ args << opts[opt].to_s
478
+
479
+ when :show_delay_options
480
+ args << "-showDelayOptions"
481
+ args << JSS.to_s_and_a(opts[opt])[:arrayform].join(', ')
482
+
483
+ when :countdown
484
+ args << "-countdown"
485
+
486
+ when :align_countdown
487
+ raise JSS::InvalidDataError, ":align_countdown must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
488
+ args << "-alignCountdown"
489
+ args << opts[opt].to_s
490
+
491
+ when :lock_hud
492
+ args << " -lockHUD "
493
+ end # case opt
494
+ end # each do opt
495
+
496
+ system JAMF_HELPER.to_s, *args
497
+ return $?.exitstatus
498
+
499
+ end # def self.jamf_helper
500
+
501
+
502
+ end # class Client
503
+
504
+ end # module