jss-api 0.5.4

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