ruby-jss 0.11.0 → 0.12.0

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.

@@ -0,0 +1,75 @@
1
+ # This is just a stub for now.
2
+
3
+ #
4
+ module JSS
5
+
6
+ #
7
+ class MobileDeviceConfigurationProfile < JSS::ConfigurationProfile
8
+
9
+
10
+ ### The base for REST resources of this class
11
+ RSRC_BASE = 'mobiledeviceconfigurationprofiles'.freeze
12
+
13
+ ### the hash key used for the JSON list output of all objects in the JSS
14
+ RSRC_LIST_KEY = :configuration_profiles
15
+
16
+ ### The hash key used for the JSON object output.
17
+ ### It's also used in various error messages
18
+ RSRC_OBJECT_KEY = :configuration_profile
19
+
20
+ # the object type for this object in
21
+ # the object history table.
22
+ # See {APIObject#add_object_history_entry}
23
+ OBJECT_HISTORY_OBJECT_TYPE = 22
24
+
25
+ # Our scopes deal with mobile_devices
26
+ SCOPE_TARGET_KEY = :mobile_devices
27
+
28
+ # icons cant be uploaded yet
29
+ # UPLOAD_TYPES = { icon: :mobiledeviceconfigurationprofileicon }.freeze
30
+
31
+ # Attributes
32
+ ###################################
33
+
34
+ # @return [Integer] how many days before a cert payload expires
35
+ # should this profile be automatically re-installed?
36
+ attr_reader :redeploy_days_before_certificate_expires
37
+
38
+ # Constructor
39
+ ###################################
40
+
41
+ # See JSS::APIObject#initialize
42
+ #
43
+ def initialize(args = {})
44
+ super
45
+ @redeploy_days_before_certificate_expires = @main_subset[:redeploy_days_before_certificate_expires]
46
+ end
47
+
48
+ # @param new_val[String] the new level for this profile (user/computer)
49
+ #
50
+ # @return [void]
51
+ #
52
+ def redeploy_days_before_certificate_expires=(new_val)
53
+ return nil if redeploy_days_before_certificate_expires == new_val
54
+ raise JSS::InvalidDataError, 'New value must be an integer >= 0' unless new_val.is_a?(Integer) && new_val >= 0
55
+ @redeploy_days_before_certificate_expires = new_val
56
+ @need_to_update = true
57
+ end #
58
+
59
+ # Private Instance Methods
60
+ ###################################
61
+ private
62
+
63
+ def rest_xml
64
+ doc = super
65
+ gen = doc.root.elements['general']
66
+ gen.add_element('redeploy_days_before_certificate_expires').text = redeploy_days_before_certificate_expires.to_s
67
+ doc.to_s
68
+ end
69
+
70
+ end # class MobileDeviceConfigurationProfile
71
+
72
+ end # module JSS
73
+
74
+ require 'jss/api_object/configuration_profile/mobile_device_configuration_profile'
75
+ require 'jss/api_object/configuration_profile/osx_configuration_profile'
@@ -0,0 +1,117 @@
1
+ # Copyright 2018 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
25
+
26
+ #
27
+ module JSS
28
+
29
+ # Classes
30
+ ###################################
31
+
32
+ # An OS X Configuration Profile in the JSS.
33
+ #
34
+ # Note that the profile payloads and the profile UUID cannot be edited or updated with this via this class.
35
+ # Use the web UI.
36
+ #
37
+ # @see JSS::APIObject
38
+ #
39
+ class OSXConfigurationProfile < JSS::ConfigurationProfile
40
+
41
+ # Class Constants
42
+ ###################################
43
+
44
+ # The base for REST resources of this class
45
+ RSRC_BASE = 'osxconfigurationprofiles'.freeze
46
+
47
+ # the hash key used for the JSON list output of all objects in the JSS
48
+ RSRC_LIST_KEY = :os_x_configuration_profiles
49
+
50
+ # The hash key used for the JSON object output.
51
+ # It's also used in various error messages
52
+ RSRC_OBJECT_KEY = :os_x_configuration_profile
53
+
54
+ # these keys, as well as :id and :name, are present in valid API JSON data for this class
55
+ VALID_DATA_KEYS = %i[distribution_method scope redeploy_on_update].freeze
56
+
57
+ # Our scopes deal with computers
58
+ SCOPE_TARGET_KEY = :computers
59
+
60
+ # Our SelfService happens on OSX
61
+ SELF_SERVICE_TARGET = :osx
62
+
63
+ # The possible values for :level
64
+ LEVELS = %w[user computer].freeze
65
+
66
+ # can not yet upload icons
67
+ # UPLOAD_TYPES = { icon: :osxconfigurationprofileicon }.freeze
68
+
69
+ # the object type for this object in
70
+ # the object history table.
71
+ # See {APIObject#add_object_history_entry}
72
+ OBJECT_HISTORY_OBJECT_TYPE = 4
73
+
74
+ # Attributes
75
+ ###################################
76
+
77
+ # @return [String] the level (user/computer) of this profile
78
+ attr_reader :level
79
+
80
+ # Constructor
81
+ ###################################
82
+
83
+ # See JSS::APIObject#initialize
84
+ #
85
+ def initialize(args = {})
86
+ super
87
+ @level = @main_subset[:level]
88
+ end
89
+
90
+ # Public Instance Methods
91
+ ###################################
92
+
93
+ # @param new_val[String] the new level for this profile (user/computer)
94
+ #
95
+ # @return [void]
96
+ #
97
+ def level=(new_val)
98
+ return nil if @level == new_val
99
+ raise JSS::InvalidDataError, "New value must be one of '#{LEVELS.join("' '")}'" unless LEVELS.include? new_val
100
+ @level = new_val
101
+ @need_to_update = true
102
+ end #
103
+
104
+ # Private Instance Methods
105
+ ###################################
106
+ private
107
+
108
+ def rest_xml
109
+ doc = super
110
+ gen = doc.root.elements['general']
111
+ gen.add_element('level').text = level
112
+ doc.to_s
113
+ end
114
+
115
+ end # class OSXConfigurationProfile
116
+
117
+ end # module
@@ -760,6 +760,7 @@ module JSS
760
760
  send_mdm_command targets, :device_name, opts: { device_name: name }, api: api
761
761
  end
762
762
  alias set_name device_name
763
+ alias set_device_name device_name
763
764
 
764
765
  # Send a wallpaper command to one or more targets
765
766
  #
@@ -1153,6 +1154,7 @@ module JSS
1153
1154
  self.class.device_name @id, name, api: @api
1154
1155
  end
1155
1156
  alias set_name device_name
1157
+ alias set_device_name device_name
1156
1158
 
1157
1159
  # Send a wallpaper command to this object
1158
1160
  #
@@ -524,7 +524,7 @@ module JSS
524
524
  def update
525
525
  super
526
526
  return unless @needs_mdm_name_change
527
- set_device_mame @name if managed? && supervised?
527
+ set_device_name @name if managed? && supervised?
528
528
  @needs_mdm_name_change = false
529
529
  end
530
530
 
@@ -63,7 +63,7 @@ module JSS
63
63
  # Classes including this module *MUST*:
64
64
  # - call {#add_self_service_xml(xmldoc)} in their #rest_xml method
65
65
  #
66
- # IMPORTANT: Since SelfServable also includes #{JSS::Updatable}, for uploading icons,
66
+ # IMPORTANT: Since SelfServable also includes #{JSS::Uploadable}, for uploading icons,
67
67
  # see that module for its requirements.
68
68
  #
69
69
  #
@@ -90,6 +90,35 @@ module JSS
90
90
 
91
91
  DEFAULT_INSTALL_BUTTON_TEXT = 'Install'.freeze
92
92
 
93
+ # This hash contains the details about the inconsistencies of how
94
+ # Self Service data is dealt with in the API data of the different
95
+ # self-servable classes.
96
+ #
97
+ # - in_self_service_data_path: Array, In the API data hash (the @init_data)
98
+ # where to find the value indicicating that a thing is in self service.
99
+ # e.g. [:self_service, :use_for_self_service] means
100
+ # @init_data[:self_service][:use_for_self_service]
101
+ #
102
+ # - in_self_service: Object, In the path defined above, what value means
103
+ # the thing IS in self service
104
+ #
105
+ # - not_in_self_service: Object, In the path defined above, what value means
106
+ # the thing IS NOT in self service
107
+ #
108
+ # - targets: Array<Symbol>, the array contains either :macos, :ios, or both.
109
+ #
110
+ # - payload: Symbol, The thing that is deployed by self service, one of:
111
+ # :policy, :app, :profile, :patchpolicy (ebooks are considered apps)
112
+ #
113
+ # - can_display_in_categories: Boolean, when adding 'self service categories'
114
+ # can the thing be 'displayed in' those categories?
115
+ #
116
+ # - can_feature_in_categories: Boolean, when adding 'self service categories'
117
+ # can the thing be 'featured in' those categories?
118
+ #
119
+ # It's unfortunate that this is needed in order to keep all the
120
+ # self service ruby code in this one module.
121
+ #
93
122
  SELF_SERVICE_CLASSES = {
94
123
  JSS::Policy => {
95
124
  in_self_service_data_path: [:self_service, :use_for_self_service],
@@ -330,7 +359,11 @@ module JSS
330
359
  # @return [void]
331
360
  #
332
361
  def self_service_user_removable=(new_val, pw = @self_service_removal_password)
333
- return nil if new_val == @self_service_user_removable && pw == @self_service_removal_password
362
+ new_val, pw = *new_val if new_val.is_a? Array
363
+ pw = nil unless new_val == :with_auth
364
+
365
+ return nil if new_val == self_service_user_removable && pw == self_service_removal_password
366
+
334
367
  validate_user_removable new_val
335
368
 
336
369
  @self_service_user_removable = new_val
@@ -354,12 +387,12 @@ module JSS
354
387
  #
355
388
  def icon=(new_icon)
356
389
  if new_icon.is_a? Integer
357
- return if new_icon == @icon.id
390
+ return if @icon && new_icon == @icon.id
358
391
  validate_icon new_icon
359
392
  @new_icon_id = new_icon
360
393
  @need_to_update = true
361
394
  else
362
- unless uploadable? && self.class::UPLOAD_TYPES.keys.include?(:icon)
395
+ unless uploadable? && defined?(self.class::UPLOAD_TYPES) && self.class::UPLOAD_TYPES.keys.include?(:icon)
363
396
  raise JSS::UnsupportedError, "Class #{self.class} does not support icon uploads."
364
397
  end
365
398
  new_icon = Pathname.new new_icon
@@ -473,7 +506,7 @@ module JSS
473
506
  @init_data[subsection][key] == @self_service_data_config[:in_self_service]
474
507
  end
475
508
 
476
- def validate_user_removable=(new_val)
509
+ def validate_user_removable(new_val)
477
510
  raise JSS::UnsupportedError, 'User removal settings not applicable to this class' unless self_service_payload == :profile
478
511
 
479
512
  raise JSS::UnsupportedError, 'Removal :with_auth not applicable to this class' if new_val == :with_auth && !self_service_targets.include?(:ios)
@@ -483,7 +516,7 @@ module JSS
483
516
 
484
517
  def validate_icon(id)
485
518
  return nil unless JSS::DB_CNX.connected?
486
- raise JSS::NoSuchItemError, "No icon with id #{new_icon}" unless JSS::Icon.all_ids.include? id
519
+ raise JSS::NoSuchItemError, "No icon with id #{id}" unless JSS::Icon.all_ids.include? id
487
520
  end
488
521
 
489
522
  # Re-read the icon data for this object from the API
@@ -533,9 +566,14 @@ module JSS
533
566
  end
534
567
 
535
568
  if self_service_payload == :profile
536
- sec = ssvc.add_element('security')
537
- sec.add_element('removal_disallowed').text = PROFILE_REMOVAL_BY_USER[@self_service_user_removable]
538
- sec.add_element('password').text = @self_service_removal_password if @self_service_removal_password
569
+ if self_service_targets.include? :ios
570
+ sec = ssvc.add_element('security')
571
+ sec.add_element('removal_disallowed').text = PROFILE_REMOVAL_BY_USER[@self_service_user_removable]
572
+ sec.add_element('password').text = @self_service_removal_password.to_s
573
+ else
574
+ gen = doc_root.elements['general']
575
+ gen.add_element('user_removable').text = (@self_service_user_removable == :always).to_s
576
+ end
539
577
  end
540
578
 
541
579
  return unless @self_service_data_config[:in_self_service_data_path]
data/lib/jss/client.rb CHANGED
@@ -26,16 +26,7 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
- # Module Variables
30
- #####################################
31
-
32
- # Module Methods
33
- #####################################
34
-
35
- # Classes
36
- #####################################
37
-
38
- # This class represents a Casper/JSS Client computer, on which
29
+ # This class represents a Jamf/JSS Client computer, on which
39
30
  # this code is running.
40
31
  #
41
32
  # Since the class represents the current machine, there's no need
@@ -43,39 +34,13 @@ module JSS
43
34
  #
44
35
  # At the moment, only Macintosh computers are supported.
45
36
  #
37
+ # TODO: convert this to a module, since that's how it's used.
46
38
  #
47
39
  class Client
48
40
 
49
- # Class Constants
41
+ # Constants
50
42
  #####################################
51
43
 
52
- # The Pathname to the jamf binary executable
53
- # As of El Capitan (OS X 10.11) the location has moved.
54
- ORIG_JAMF_BINARY = Pathname.new '/usr/sbin/jamf'
55
- ELCAP_JAMF_BINARY = Pathname.new '/usr/local/jamf/bin/jamf'
56
- JAMF_BINARY = ELCAP_JAMF_BINARY.executable? ? ELCAP_JAMF_BINARY : ORIG_JAMF_BINARY
57
-
58
- # The Pathname to the jamfHelper executable
59
- JAMF_HELPER = Pathname.new '/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper'
60
-
61
- # The window_type options for jamfHelper
62
- JAMF_HELPER_WINDOW_TYPES = {
63
- hud: 'hud',
64
- utility: 'utility',
65
- util: 'utility',
66
- full_screen: 'fs',
67
- fs: 'fs'
68
- }.freeze
69
-
70
- # The possible window positions for jamfHelper
71
- JAMF_HELPER_WINDOW_POSITIONS = [nil, :ul, :ll, :ur, :lr].freeze
72
-
73
- # The available buttons in jamfHelper
74
- JAMF_HELPER_BUTTONS = [1, 2].freeze
75
-
76
- # The possible alignment positions in jamfHelper
77
- JAMF_HELPER_ALIGNMENTS = [:right, :left, :center, :justified, :natural].freeze
78
-
79
44
  # The Pathname to the preferences plist used by the jamf binary
80
45
  JAMF_PLIST = Pathname.new '/Library/Preferences/com.jamfsoftware.jamf.plist'
81
46
 
@@ -88,25 +53,33 @@ module JSS
88
53
  # The JAMF downloads folder
89
54
  DOWNLOADS_FOLDER = JAMF_SUPPORT_FOLDER + 'Downloads'
90
55
 
91
- # These jamf commands don't need root privs (most do)
92
- ROOTLESS_JAMF_COMMANDS = [
93
- :about,
94
- :checkJSSConnection,
95
- :getARDFields,
96
- :getComputerName,
97
- :help,
98
- :listUsers,
99
- :version
100
- ].freeze
56
+ # The bin folder inside the Jamf support folder
57
+ SUPPORT_BIN_FOLDER = JAMF_SUPPORT_FOLDER + 'bin'
101
58
 
102
- #####################################
103
- # Class Variables
104
- #####################################
59
+ # The bin folder with the jamf binary and a few other things
60
+ USR_LOCAL_BIN_FOLDER = Pathname.new '/usr/local/jamf/bin'
61
+
62
+ # This command gives raw info about console users
63
+ CONSOLE_USERS_SCUTIL_CMD = 'echo "show State:/Users/ConsoleUser" | /usr/sbin/scutil'.freeze
64
+
65
+ # ignore console user = root (loginwindow)
66
+ ROOT_USER = 'root'.freeze
67
+
68
+ # ignore primary console user loginwindow
69
+ LOGINWINDOW_USER = 'loginwindow'.freeze
70
+
71
+ # The end of the path to the Self Service Executable.
72
+ # Used to figure out who's running Self Service.app
73
+ SELF_SERVICE_EXECUTABLE_END = '/Self Service.app/Contents/MacOS/Self Service'.freeze
74
+
75
+ # the ps command used to figure out who's running Self Service
76
+ PS_USER_COMM = 'ps -A -o user,comm'.freeze
105
77
 
106
- #####################################
107
78
  # Class Methods
108
79
  #####################################
109
80
 
81
+
82
+
110
83
  # Get the current IP address as a String.
111
84
  #
112
85
  # This handy code doesn't acutally make a UDP connection,
@@ -132,22 +105,6 @@ module JSS
132
105
  Socket.do_not_reverse_lookup = orig
133
106
  end
134
107
 
135
- # Who's logged in to the console right now?
136
- #
137
- # @return [String, nil] the username of the current console user, or nil if none.
138
- #
139
- def self.console_user
140
- cmd = '/usr/sbin/scutil'
141
- qry = 'show State:/Users/ConsoleUser'
142
- Open3.popen2e(cmd) do |cmdin, cmdouterr, _wait_thr|
143
- cmdin.puts qry
144
- cmdin.close
145
- out = cmdouterr.read
146
- user = out.lines.select { |l| l =~ /^\s+Name\s*:/ }.first.to_s.split(/\s*:\s*/).last
147
- return user.nil? ? user : user.chomp
148
- end # do
149
- end
150
-
151
108
  # Is the jamf binary installed?
152
109
  #
153
110
  # @return [Boolean] is the jamf binary installed?
@@ -207,11 +164,12 @@ module JSS
207
164
 
208
165
  # The contents of the JAMF plist
209
166
  #
210
- # @return [Hash] the parsed contents of the JAMF_PLIST if it exists, an empty hash if not
167
+ # @return [Hash] the parsed contents of the JAMF_PLIST if it exists,
168
+ # an empty hash if not
211
169
  #
212
170
  def self.jamf_plist
213
171
  return {} unless JAMF_PLIST.file?
214
- Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape JSS::Client::JAMF_PLIST.to_s}`
172
+ JSS.parse_plist JAMF_PLIST
215
173
  end
216
174
 
217
175
  # All the JAMF receipts on this client
@@ -264,303 +222,51 @@ module JSS
264
222
  #
265
223
  def self.hardware_data
266
224
  raw = `/usr/sbin/system_profiler SPHardwareDataType -xml 2>/dev/null`
267
- Plist.parse_xml(raw)[0]['_items'][0]
225
+ JSS.parse_plist(raw)[0]['_items'][0]
268
226
  end
269
227
 
270
- # Run an arbitrary jamf binary command.
271
- #
272
- # @note Most jamf commands require superuser/root privileges.
273
- #
274
- # @param command[String,Symbol] the jamf binary command to run
275
- # The command is the single jamf command that comes after the/usr/bin/jamf.
276
- #
277
- # @param args[String,Array] the arguments passed to the jamf command.
278
- # This is to be passed to Kernel.` (backtick), after being combined with the
279
- # jamf binary and the jamf command
280
- #
281
- # @param verbose[Boolean] Should the stdout & stderr of the jamf binary be sent to
282
- # the current stdout in realtime, as well as returned as a string?
283
- #
284
- # @return [String] the stdout & stderr of the jamf binary.
228
+ # Who's currently got an active GUI session? - might be
229
+ # more than one if Fast User Switching is in use.
285
230
  #
286
- # @example
287
- # These two are equivalent:
288
- #
289
- # JSS::Client.run_jamf "recon", "-assetTag 12345 -department 'IT Support'"
290
- #
291
- # JSS::Client.run_jamf :recon, ['-assetTag', '12345', '-department', 'IT Support'"]
231
+ # @return [Array<String>] The current users with GUI sessions
292
232
  #
233
+ def self.console_users
234
+ output = `#{CONSOLE_USERS_SCUTIL_CMD}`
235
+ userlines = output.lines.select { |l| l =~ /SessionUserNameKey\s*:/ }
236
+ userlines.map! { |ul| ul.split(':').last.strip }
237
+ userlines.reject { |un| un == ROOT_USER }
238
+ end
239
+
240
+ # Which console user is using the primary GUI console?
241
+ # Returns nil if the primary GUI console is at the login window.
293
242
  #
294
- # The details of the Process::Status for the jamf binary process can be captured from $?
295
- # immediately after calling. (See Process::Status)
243
+ # @return [String,nil] The login name of the user is using the primary
244
+ # GUI console, or nil if at the login window.
296
245
  #
297
- def self.run_jamf(command, args = nil, verbose = false)
298
- raise JSS::UnmanagedError, 'The jamf binary is not installed on this computer.' unless installed?
299
- raise JSS::UnsupportedError, 'You must have root privileges to run that jamf binary command' unless \
300
- ROOTLESS_JAMF_COMMANDS.include?(command.to_sym) || JSS.superuser?
301
-
302
- cmd = case args
303
- when nil
304
- "#{JAMF_BINARY} #{command}"
305
- when String
306
- "#{JAMF_BINARY} #{command} #{args}"
307
- when Array
308
- ([JAMF_BINARY.to_s, command] + args).join(' ').to_s
309
- else
310
- raise JSS::InvalidDataError, 'args must be a String or Array of Strings'
311
- end # case
312
-
313
- cmd += ' -verbose' if verbose && (!cmd.include? ' -verbose')
314
- puts "Running: #{cmd}" if verbose
246
+ def self.primary_console_user
247
+ `#{CONSOLE_USERS_SCUTIL_CMD}` =~ /^\s*Name : (\S+)$/
248
+ Regexp.last_match(1) == LOGINWINDOW_USER ? nil : user
249
+ end
315
250
 
316
- output = []
317
- IO.popen("#{cmd} 2>&1") do |proc|
318
- while line = proc.gets
319
- output << line
320
- puts line if verbose
321
- end
322
- end
323
- install_out = output.join('')
324
- install_out.force_encoding('UTF-8') if install_out.respond_to? :force_encoding
325
- install_out
326
- end # run_jamf
251
+ # alias for primary_console_user
252
+ def self.console_user
253
+ primary_console_user
254
+ end
327
255
 
328
- # A wrapper for the jamfHelper command, which can display a window on the client machine.
329
- #
330
- # The first parameter must be a symbol defining what kind of window to display. The options are
331
- # - :hud - creates an Apple "Heads Up Display" style window
332
- # - :utility or :util - creates an Apple "Utility" style window
333
- # - :fs or :full_screen or :fullscreen - creates a full screen window that restricts all user input
334
- # WARNING: Remote access must be used to unlock machines in this mode
335
- #
336
- # The remaining options Hash can contain any of the options listed. See below for descriptions.
337
- #
338
- # The value returned is the Integer exitstatus/stdout (both are the same) of the jamfHelper command.
339
- # The meanings of those integers are:
340
- #
341
- # - 0 - Button 1 was clicked
342
- # - 1 - The Jamf Helper was unable to launch
343
- # - 2 - Button 2 was clicked
344
- # - 3 - Process was started as a launchd task
345
- # - XX1 - Button 1 was clicked with a value of XX seconds selected in the drop-down
346
- # - XX2 - Button 2 was clicked with a value of XX seconds selected in the drop-down
347
- # - 239 - The exit button was clicked
348
- # - 240 - The "ProductVersion" in sw_vers did not return 10.5.X, 10.6.X or 10.7.X
349
- # - 243 - The window timed-out with no buttons on the screen
350
- # - 250 - Bad "-windowType"
351
- # - 254 - Cancel button was select with delay option present
352
- # - 255 - No "-windowType"
353
- #
354
- # If the :abandon_process option is given, the integer returned is the Process ID
355
- # of the abondoned process running jamfHelper.
356
- #
357
- # See also /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -help
358
- #
359
- # @note the -startlaunchd and -kill options are not available in this implementation, since
360
- # they don't work at the moment (casper 9.4).
361
- # -startlaunchd seems to be required to NOT use launchd, and when it's ommited, an error is generated
362
- # about the launchd plist permissions being incorrect.
363
- #
364
- # @param window_type[Symbol] The type of window to display
365
- #
366
- # @param opts[Hash] the options for the window
367
- #
368
- # @option opts :window_position [Symbol,nil] one of [ nil, :ul, :ll. :ur, :lr ]
369
- # Positions window in the upper right, upper left, lower right or lower left of the user's screen
370
- # If no input is given, the window defaults to the center of the screen
371
- #
372
- # @option opts :title [String]
373
- # Sets the window's title to the specified string
374
- #
375
- # @option opts :heading [String]
376
- # Sets the heading of the window to the specified string
377
- #
378
- # @option opts :align_heading [Symbol] one of [:right, :left, :center, :justified, :natural]
379
- # Aligns the heading to the specified alignment
380
- #
381
- # @option opts :description [String]
382
- # Sets the main contents of the window to the specified string
383
- #
384
- # @option opts :align_description [Symbol] one of [:right, :left, :center, :justified, :natural]
385
- # Aligns the description to the specified alignment
386
- #
387
- # @option opts :icon [String,Pathname]
388
- # Sets the windows image field to the image located at the specified path
389
- #
390
- # @option opts :icon_size [Integer]
391
- # Changes the image frame to the specified pixel size
392
- #
393
- # @option opts :full_screen_icon [any value]
394
- # Scales the "icon" to the full size of the window.
395
- # Note: Only available in full screen mode
256
+ # Who's currently running Self Service.app? - might be
257
+ # more than one if Fast User Switching is in use.
396
258
  #
397
- # @option opts :button1 [String]
398
- # Creates a button with the specified label
259
+ # @return [Array<String>] The current users running Self Service.app
399
260
  #
400
- # @option opts :button2 [String]
401
- # Creates a second button with the specified label
402
- #
403
- # @option opts :default_button [Integer] either 1 or 2
404
- # Sets the default button of the window to the specified button. The Default Button will respond to "return"
405
- #
406
- # @option opts :cancel_button [Integer] either 1 or 2
407
- # Sets the cancel button of the window to the specified button. The Cancel Button will respond to "escape"
408
- #
409
- # @option opts :timeout [Integer]
410
- # Causes the window to timeout after the specified amount of seconds
411
- # Note: The timeout will cause the default button, button 1 or button 2 to be selected (in that order)
412
- #
413
- # @option opts :show_delay_options [String,Array<Integer>] A String of comma-separated Integers, or an Array of Integers.
414
- # Enables the "Delay Options Mode". The window will display a dropdown with the values passed through the string
415
- #
416
- # @option opts :countdown [any value]
417
- # Displays a string notifying the user when the window will time out
418
- #
419
- # @option opts :align_countdown [Symbol] one of [:right, :left, :center, :justified, :natural]
420
- # Aligns the countdown to the specified alignment
421
- #
422
- # @option opts :lock_hud [Boolean]
423
- # Removes the ability to exit the HUD by selecting the close button
424
- #
425
- # @option opts :abandon_process [Boolean] Abandon the jamfHelper process so that your code can exit.
426
- # This is mostly used so that a policy can finish while a dialog is waiting
427
- # (possibly forever) for user response. When true, the returned value is the
428
- # process id of the abandoned jamfHelper process.
429
- #
430
- # @option opts :output_file [String, Pathname] Save the output of jamfHelper
431
- # (the exit code) into this file. This is useful when using abandon_process.
432
- # The output file can be examined later to see what happened. If this option
433
- # is not provided, no output is saved.
434
- #
435
- # @option opts :arg_string [String] The jamfHelper commandline args as a single
436
- # String, the way you'd specify them in a shell. This is appended to any
437
- # Ruby options provided when calling the method. So calling:
438
- # JSS::Client.jamf_helper :hud, title: 'This is a title', arg_string: '-heading "this is a heading"'
439
- # will run
440
- # jamfHelper -windowType hud -title 'this is a title' -heading "this is a heading"
441
- # When using this, be careful not to specify the windowType, since it's generated
442
- # by the first, required, parameter of this method.
443
- #
444
- # @return [Integer] the exit status of the jamfHelper command. See above.
445
- #
446
- def self.jamf_helper(window_type = :hud, opts = {})
447
- raise JSS::UnmanagedError, 'The jamfHelper app is not installed properly on this computer.' unless JAMF_HELPER.executable?
448
-
449
- unless JAMF_HELPER_WINDOW_TYPES.include? window_type
450
- raise JSS::InvalidDataError, "The first parameter must be a window type, one of :#{JAMF_HELPER_WINDOW_TYPES.keys.join(', :')}."
451
- end
452
-
453
- # start building the arg array
454
-
455
- args = ['-startlaunchd', '-windowType', JAMF_HELPER_WINDOW_TYPES[window_type]]
456
-
457
- opts.keys.each do |opt|
458
- case opt
459
- when :window_position
460
- raise JSS::InvalidDataError, ":window_position must be one of :#{JAMF_HELPER_WINDOW_POSITIONS.join(', :')}." unless \
461
- JAMF_HELPER_WINDOW_POSITIONS.include? opts[opt].to_sym
462
- args << '-windowPosition'
463
- args << opts[opt].to_s
464
-
465
- when :title
466
- args << '-title'
467
- args << opts[opt].to_s
468
-
469
- when :heading
470
- args << '-heading'
471
- args << opts[opt].to_s
472
-
473
- when :align_heading
474
- raise JSS::InvalidDataError, ":align_heading must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \
475
- JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
476
- args << '-alignHeading'
477
- args << opts[opt].to_s
478
-
479
- when :description
480
- args << '-description'
481
- args << opts[opt].to_s
482
-
483
- when :align_description
484
- raise JSS::InvalidDataError, ":align_description must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \
485
- JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
486
- args << '-alignDescription'
487
- args << opts[opt].to_s
488
-
489
- when :icon
490
- args << '-icon'
491
- args << opts[opt].to_s
492
-
493
- when :icon_size
494
- args << '-iconSize'
495
- args << opts[opt].to_s
496
-
497
- when :full_screen_icon
498
- args << '-fullScreenIcon'
499
-
500
- when :button1
501
- args << '-button1'
502
- args << opts[opt].to_s
503
-
504
- when :button2
505
- args << '-button2'
506
- args << opts[opt].to_s
507
-
508
- when :default_button
509
- raise JSS::InvalidDataError, ":default_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless \
510
- JAMF_HELPER_BUTTONS.include? opts[opt]
511
- args << '-defaultButton'
512
- args << opts[opt].to_s
513
-
514
- when :cancel_button
515
- raise JSS::InvalidDataError, ":cancel_button must be one of #{JAMF_HELPER_BUTTONS.join(', ')}." unless \
516
- JAMF_HELPER_BUTTONS.include? opts[opt]
517
- args << '-cancelButton'
518
- args << opts[opt].to_s
519
-
520
- when :timeout
521
- args << '-timeout'
522
- args << opts[opt].to_s
523
-
524
- when :show_delay_options
525
- args << '-showDelayOptions'
526
- args << JSS.to_s_and_a(opts[opt])[:arrayform].join(', ')
527
-
528
- when :countdown
529
- args << '-countdown' if opts[opt]
530
-
531
- when :align_countdown
532
- raise JSS::InvalidDataError, ":align_countdown must be one of :#{JAMF_HELPER_ALIGNMENTS.join(', :')}." unless \
533
- JAMF_HELPER_ALIGNMENTS.include? opts[opt].to_sym
534
- args << '-alignCountdown'
535
- args << opts[opt].to_s
536
-
537
- when :lock_hud
538
- args << '-lockHUD' if opts[opt]
539
-
540
- end # case opt
541
- end # each do opt
542
-
543
- cmd = Shellwords.escape JAMF_HELPER.to_s
544
- args.each { |arg| cmd << " #{Shellwords.escape arg}" }
545
- cmd << " #{opts[:arg_string]}" if opts[:arg_string]
546
- cmd << " > #{Shellwords.escape opts[:output_file]}" if opts[:output_file]
547
-
548
- if opts[:abandon_process]
549
- pid = Process.fork
550
- if pid.nil?
551
- # In child
552
- exec cmd
553
- else
554
- # In parent
555
- Process.detach(pid)
556
- pid
557
- end
558
- else
559
- system cmd
560
- $CHILD_STATUS.exitstatus
561
- end
562
- end # def self.jamf_helper
261
+ def self.self_service_users
262
+ ss_userlines = `#{PS_USER_COMM}`.lines.select { |l| l.include? SELF_SERVICE_EXECUTABLE_END }
263
+ ss_userlines.map { |ssl| ssl.split(' ').first }
264
+ end
563
265
 
564
266
  end # class Client
565
267
 
566
268
  end # module
269
+
270
+ require 'jss/client/jamf_binary'
271
+ require 'jss/client/jamf_helper'
272
+ require 'jss/client/management_action'