ruby-jss 0.6.5 → 0.6.6

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +57 -5
  3. data/lib/jss.rb +78 -94
  4. data/lib/jss/api_connection.rb +8 -0
  5. data/lib/jss/api_object.rb +126 -102
  6. data/lib/jss/api_object/creatable.rb +33 -15
  7. data/lib/jss/api_object/distribution_point.rb +4 -1
  8. data/lib/jss/api_object/extension_attribute.rb +1 -1
  9. data/lib/jss/api_object/package.rb +121 -187
  10. data/lib/jss/api_object/policy.rb +590 -251
  11. data/lib/jss/api_object/script.rb +92 -128
  12. data/lib/jss/api_object/self_servable.rb +93 -117
  13. data/lib/jss/api_object/updatable.rb +12 -27
  14. data/lib/jss/api_object/uploadable.rb +12 -15
  15. data/lib/jss/client.rb +178 -156
  16. data/lib/jss/db_connection.rb +34 -49
  17. data/lib/jss/ruby_extensions/string.rb +25 -21
  18. data/lib/jss/version.rb +1 -1
  19. data/lib/jss/webhooks.rb +52 -0
  20. data/lib/jss/webhooks/README.md +269 -0
  21. data/lib/jss/webhooks/configuration.rb +212 -0
  22. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation-executable +90 -0
  23. data/lib/jss/webhooks/data/sample_handlers/RestAPIOperation.rb +44 -0
  24. data/lib/jss/webhooks/data/sample_jsons/ComputerAdded.json +27 -0
  25. data/lib/jss/webhooks/data/sample_jsons/ComputerCheckIn.json +27 -0
  26. data/lib/jss/webhooks/data/sample_jsons/ComputerInventoryCompleted.json +27 -0
  27. data/lib/jss/webhooks/data/sample_jsons/ComputerPolicyFinished.json +27 -0
  28. data/lib/jss/webhooks/data/sample_jsons/ComputerPushCapabilityChanged.json +27 -0
  29. data/lib/jss/webhooks/data/sample_jsons/JSSShutdown.json +14 -0
  30. data/lib/jss/webhooks/data/sample_jsons/JSSStartup.json +14 -0
  31. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCheckIn.json +26 -0
  32. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceCommandCompleted.json +26 -0
  33. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceEnrolled.json +26 -0
  34. data/lib/jss/webhooks/data/sample_jsons/MobileDevicePushSent.json +26 -0
  35. data/lib/jss/webhooks/data/sample_jsons/MobileDeviceUnEnrolled.json +26 -0
  36. data/lib/jss/webhooks/data/sample_jsons/PatchSoftwareTitleUpdated.json +14 -0
  37. data/lib/jss/webhooks/data/sample_jsons/PushSent.json +11 -0
  38. data/lib/jss/webhooks/data/sample_jsons/RestAPIOperation.json +15 -0
  39. data/lib/jss/webhooks/data/sample_jsons/SCEPChallenge.json +10 -0
  40. data/lib/jss/webhooks/data/sample_jsons/SmartGroupComputerMembershipChange.json +13 -0
  41. data/lib/jss/webhooks/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +13 -0
  42. data/lib/jss/webhooks/event.rb +138 -0
  43. data/lib/jss/webhooks/event/computer_added.rb +37 -0
  44. data/lib/jss/webhooks/event/computer_check_in.rb +37 -0
  45. data/lib/jss/webhooks/event/computer_inventory_completed.rb +37 -0
  46. data/lib/jss/webhooks/event/computer_policy_finished.rb +37 -0
  47. data/lib/jss/webhooks/event/computer_push_capability_changed.rb +37 -0
  48. data/lib/jss/webhooks/event/handlers.rb +191 -0
  49. data/lib/jss/webhooks/event/jss_shutdown.rb +37 -0
  50. data/lib/jss/webhooks/event/jss_startup.rb +37 -0
  51. data/lib/jss/webhooks/event/mobile_device_check_in.rb +37 -0
  52. data/lib/jss/webhooks/event/mobile_device_command_completed.rb +37 -0
  53. data/lib/jss/webhooks/event/mobile_device_enrolled.rb +37 -0
  54. data/lib/jss/webhooks/event/mobile_device_push_sent.rb +37 -0
  55. data/lib/jss/webhooks/event/mobile_device_unenrolled.rb +37 -0
  56. data/lib/jss/webhooks/event/patch_software_title_updated.rb +37 -0
  57. data/lib/jss/webhooks/event/push_sent.rb +37 -0
  58. data/lib/jss/webhooks/event/rest_api_operation.rb +37 -0
  59. data/lib/jss/webhooks/event/scep_challenge.rb +37 -0
  60. data/lib/jss/webhooks/event/smart_group_computer_membership_change.rb +37 -0
  61. data/lib/jss/webhooks/event/smart_group_mobile_device_membership_change.rb +37 -0
  62. data/lib/jss/webhooks/event/webhook.rb +39 -0
  63. data/lib/jss/webhooks/event_objects.rb +111 -0
  64. data/lib/jss/webhooks/event_objects/computer.rb +48 -0
  65. data/lib/jss/webhooks/event_objects/jss.rb +35 -0
  66. data/lib/jss/webhooks/event_objects/mobile_device.rb +47 -0
  67. data/lib/jss/webhooks/event_objects/patch_software_title_update.rb +37 -0
  68. data/lib/jss/webhooks/event_objects/push.rb +32 -0
  69. data/lib/jss/webhooks/event_objects/rest_api_operation.rb +36 -0
  70. data/lib/jss/webhooks/event_objects/scep_challenge.rb +31 -0
  71. data/lib/jss/webhooks/event_objects/smart_group.rb +34 -0
  72. data/lib/jss/webhooks/server_app.rb +36 -0
  73. data/lib/jss/webhooks/server_app/routes.rb +26 -0
  74. data/lib/jss/webhooks/server_app/routes/handle_webhook_event.rb +38 -0
  75. data/lib/jss/webhooks/server_app/routes/home.rb +36 -0
  76. data/lib/jss/webhooks/server_app/self_signed_cert.rb +64 -0
  77. data/lib/jss/webhooks/server_app/server.rb +59 -0
  78. data/lib/jss/webhooks/version.rb +31 -0
  79. metadata +63 -4
@@ -1,49 +1,42 @@
1
1
  ### Copyright 2016 Pixar
2
- ###
2
+ ###
3
3
  ### Licensed under the Apache License, Version 2.0 (the "Apache License")
4
4
  ### with the following modification; you may not use this file except in
5
5
  ### compliance with the Apache License and the following modification to it:
6
6
  ### Section 6. Trademarks. is deleted and replaced with:
7
- ###
7
+ ###
8
8
  ### 6. Trademarks. This License does not grant permission to use the trade
9
9
  ### names, trademarks, service marks, or product names of the Licensor
10
10
  ### and its affiliates, except as required to comply with Section 4(c) of
11
11
  ### the License and to reproduce the content of the NOTICE file.
12
- ###
12
+ ###
13
13
  ### You may obtain a copy of the Apache License at
14
- ###
14
+ ###
15
15
  ### http://www.apache.org/licenses/LICENSE-2.0
16
- ###
16
+ ###
17
17
  ### Unless required by applicable law or agreed to in writing, software
18
18
  ### distributed under the Apache License with the above modification is
19
19
  ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
20
  ### KIND, either express or implied. See the Apache License for the specific
21
21
  ### language governing permissions and limitations under the Apache License.
22
- ###
22
+ ###
23
23
  ###
24
24
 
25
25
  ###
26
26
  module JSS
27
27
 
28
- ######################
29
28
  ### Module Constants
30
29
  ######################
31
30
 
32
-
33
- ######################
34
31
  ### Module Variables
35
32
  ######################
36
33
 
37
- ######################
38
34
  ### Module Methods
39
35
  ######################
40
36
 
41
-
42
- #####################################
43
37
  ### Classes
44
38
  #####################################
45
39
 
46
- ###
47
40
  ### A class implementing a JSS Policy.
48
41
  ###
49
42
  ### Like many API objects, the data comes from the API in sections, and
@@ -52,8 +45,8 @@ module JSS
52
45
  ###
53
46
  ###
54
47
  ### Policy instances are partially read-only:
55
- ### - New policies cannot be created via this class, please use the WebApp.
56
- ### - Only a few attributes can be changed and updated via the Policy class:
48
+ ### - Due to limitations in the API implementation of policies, as well as the complexity
49
+ ### of policy objects, only these attributes can be set and updated via the Policy class:
57
50
  ### - - name
58
51
  ### - - frequency
59
52
  ### - - target_drive
@@ -63,21 +56,25 @@ module JSS
63
56
  ### - - triggers
64
57
  ### - - scope, see {JSS::Scopable} and {JSS::Scopable::Scope}
65
58
  ### - - files and processes
59
+ ### - - packages, see {#add_package} and {#remove_package}
60
+ ### - - scripts see {#add_script} and {#remove_script}
61
+ ### - - self service, see {JSS::SelfServable}
62
+ ###
66
63
  ### All other values and sections must be edited via the Web App.
67
64
  ###
68
65
  ### Policies may be deleted via this class
69
66
  ###
70
- ### @see JSS::APIObject
71
- ###
72
67
  class Policy < JSS::APIObject
73
68
 
74
69
  #####################################
75
70
  ### Mix-Ins
76
71
  #####################################
77
72
 
73
+ include JSS::Creatable
78
74
  include JSS::Updatable
79
75
  include JSS::Scopable
80
76
  include JSS::Uploadable
77
+ include JSS::SelfServable
81
78
 
82
79
  #####################################
83
80
  ### Class Methods
@@ -87,9 +84,8 @@ module JSS
87
84
  ### Class Constants
88
85
  #####################################
89
86
 
90
-
91
87
  ### The base for REST resources of this class
92
- RSRC_BASE = "policies"
88
+ RSRC_BASE = 'policies'.freeze
93
89
 
94
90
  ### the hash key used for the JSON list output of all objects in the JSS
95
91
  RSRC_LIST_KEY = :policies
@@ -99,8 +95,16 @@ module JSS
99
95
  RSRC_OBJECT_KEY = :policy
100
96
 
101
97
  ### these keys, as well as :id and :name, are present in valid API JSON data for this class
102
- VALID_DATA_KEYS = [:scope, :user_interaction, :files_processes ]
98
+ VALID_DATA_KEYS = [:scope, :user_interaction, :files_processes].freeze
103
99
 
100
+ ### policies can take uploaded icons
101
+ UPLOAD_TYPES = { icon: :policies }.freeze
102
+
103
+ ### policies are available in macOS self Serviec
104
+ SELF_SERVICE_TARGET = :macos
105
+
106
+ ### policies via self services are still polcies
107
+ SELF_SERVICE_PAYLOAD = :policy
104
108
 
105
109
  SECTIONS = [
106
110
  :general,
@@ -116,72 +120,89 @@ module JSS
116
120
  :dock_items,
117
121
  :disk_encryption,
118
122
  :printers
119
- ]
123
+ ].freeze
120
124
 
121
125
  FREQUENCIES = {
122
- :ongoing => "Ongoing",
123
- :once_per_computer => "Once per computer",
124
- :once_per_user =>"Once per user",
125
- :daily => "Once every day",
126
- :weekly => "Once every week",
127
- :monthly => "Once every month"
128
- }
126
+ ongoing: 'Ongoing',
127
+ once_per_computer: 'Once per computer',
128
+ once_per_user: 'Once per user',
129
+ daily: 'Once every day',
130
+ weekly: 'Once every week',
131
+ monthly: 'Once every month'
132
+ }.freeze
129
133
 
130
134
  RESTART_WHEN = {
131
- :if_pkg_requires => "Restart if a package or update requires it",
132
- :now => "Restart immediately",
133
- :delayed => "Restart",
134
- :dont => "Do not restart"
135
- }
135
+ if_pkg_requires: 'Restart if a package or update requires it',
136
+ now: 'Restart immediately',
137
+ delayed: 'Restart',
138
+ dont: 'Do not restart'
139
+ }.freeze
136
140
 
137
141
  RESTART_DISKS = {
138
- :current => "Current Startup Disk",
139
- :selected => "Currently Selected Startup Disk (No Bless)",
140
- :netboot => "NetBoot",
141
- :os_installer => "inPlaceOSUpgradeDirectory"
142
- } # Note: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
142
+ current: 'Current Startup Disk',
143
+ selected: 'Currently Selected Startup Disk (No Bless)',
144
+ netboot: 'NetBoot',
145
+ os_installer: 'inPlaceOSUpgradeDirectory'
146
+ }.freeze # Note: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
143
147
 
144
148
  ACCOUNT_ACTIONS = {
145
- :create => "Create",
146
- :change_pw => "Reset",
147
- :delete => "Delete",
148
- :disable_fv2 => "DisableFileVault"
149
- }
149
+ create: 'Create',
150
+ change_pw: 'Reset',
151
+ delete: 'Delete',
152
+ disable_fv2: 'DisableFileVault'
153
+ }.freeze
150
154
 
151
155
  MGMT_ACCOUNT_ACTIONS = {
152
- :no_change => "doNotChange",
153
- :change_pw => "specified",
154
- :generate_pw => "random",
155
- :enable_fv2 => "fileVaultEnable",
156
- :disable_fv2 => "fileVaultDisable"
157
- }
156
+ no_change: 'doNotChange',
157
+ change_pw: 'specified',
158
+ generate_pw: 'random',
159
+ enable_fv2: 'fileVaultEnable',
160
+ disable_fv2: 'fileVaultDisable'
161
+ }.freeze
158
162
 
159
163
  PACKAGE_ACTIONS = {
160
- :install => "Install",
161
- :remove => "Uninstall",
162
- :cache =>"Cache",
163
- :install_cache => "Install Cached"
164
- }
165
-
166
- SCRIPT_PRIORITIES = {:pre => "Before", :post => "After"}
167
-
168
- PRINTER_ACTIIONS = {:map => "install", :unmap => "uninstall"}
169
-
170
- DOCK_ITEM_ACTIONS = {:add_start => "Add To Beginning", :add_end => "Add To End", :remove => "Remove"}
171
-
172
- NETWORK_REQUIREMENTS = {:any => "Any", :ethernet => "EtherNet"}
164
+ install: 'Install',
165
+ remove: 'Uninstall',
166
+ cache: 'Cache',
167
+ install_cache: 'Install Cached'
168
+ }.freeze
169
+
170
+ SCRIPT_PRIORITIES = {
171
+ pre: 'Before',
172
+ before: 'Before',
173
+ post: 'After',
174
+ after: 'After'
175
+ }.freeze
176
+
177
+ PRINTER_ACTIIONS = {
178
+ map: 'install',
179
+ unmap: 'uninstall'
180
+ }.freeze
181
+
182
+ DOCK_ITEM_ACTIONS = {
183
+ add_start: 'Add To Beginning',
184
+ add_end: 'Add To End', remove: 'Remove'
185
+ }.freeze
186
+
187
+ NETWORK_REQUIREMENTS = {
188
+ any: 'Any',
189
+ ethernet: 'EtherNet'
190
+ }.freeze
191
+
192
+ TRIGGER_TYPES = {
193
+ event: 'EVENT',
194
+ user: 'USER_INITIATED'
195
+ }.freeze
173
196
 
174
197
  TRIGGER_EVENTS = {
175
- :startup => :trigger_startup,
176
- :login => :trigger_login,
177
- :logout => :trigger_logout,
178
- :checkin => :trigger_checkin,
179
- :network_state => :trigger_network_state_changed ,
180
- :enrollment => :trigger_enrollment_complete ,
181
- :custom => :trigger_other
182
- }
183
-
184
- TRIGGER_TYPES = {:event => "EVENT", :user => "USER_INITIATED"}
198
+ startup: :trigger_startup,
199
+ login: :trigger_login,
200
+ logout: :trigger_logout,
201
+ checkin: :trigger_checkin,
202
+ network_state: :trigger_network_state_changed,
203
+ enrollment: :trigger_enrollment_complete,
204
+ custom: :trigger_other
205
+ }.freeze
185
206
 
186
207
  SCOPE_TARGET_KEY = :computers
187
208
 
@@ -213,12 +234,27 @@ module JSS
213
234
  ### @return [String] a string with the site name
214
235
  attr_reader :site
215
236
 
216
-
217
237
  ### @return [Hash]
218
238
  ###
219
239
  ### Overrides for various defaults
220
240
  ###
221
- ### The hash looks like: !{ :distribution_point => "", :force_afp_smb => false, :netboot_server => "current", :target_drive => "default", :sus => "default"}
241
+ ### NOTE: There's an API bug in both XML and JSON with the
242
+ ### :distribution_point and :target_drive values.
243
+ ### First off, it's not clear what the :target_drive value here
244
+ ### is overriding, since there's a :target_drive value in the
245
+ ### main General hash.
246
+ ### Second off - when you set a non-default dist.point in the
247
+ ### packages section of the UI, that value shows up in both
248
+ ### this :target_drive and the general one, but the :distribution_point
249
+ ### value here stays empty.
250
+ ###
251
+ ### The hash looks like:
252
+ ### :distribution_point => "",
253
+ ### :force_afp_smb => false,
254
+ ### :netboot_server => "current",
255
+ ### :target_drive => "default",
256
+ ### :sus => "default"
257
+ ###
222
258
  attr_reader :override_default_settings
223
259
 
224
260
  ### The API has a :network_requirements key in the general section, but
@@ -304,10 +340,11 @@ module JSS
304
340
  ###
305
341
  attr_reader :trigger_events
306
342
 
307
- ##### client machine maintenence
343
+ ###### client machine maintenence
308
344
  ### These are the computer maint. tasks
309
345
  ### that might be performed by this policy
310
346
  ### All are boolean
347
+ ### TODO: make individial getters/setters as for @files_processes
311
348
 
312
349
  ### @return [Boolean] client maintenance task
313
350
  attr_reader :verify_startup_disk
@@ -333,20 +370,14 @@ module JSS
333
370
  ### @return [Boolean] client maintenance task
334
371
  attr_reader :flush_user_cache
335
372
 
336
- ### attr_reader :heal # deprecated
337
- ### attr_reader :prebinding # deprecated
338
-
339
- ##### client account maint
340
- ### acct related maintenence performed by this policy
341
-
342
373
  ### @return [Array<Hash>]
343
374
  ###
344
375
  ### The directory bindings applied
345
376
  ###
346
377
  ### each hash is like: !{:name => "LDAP", :id => 4}
378
+ ### TODO: handle as for packages & scripts
347
379
  attr_reader :directory_bindings
348
380
 
349
-
350
381
  ### @return [Hash] the open firmware mode and password
351
382
  attr_reader :open_firmware_efi_password
352
383
 
@@ -361,6 +392,7 @@ module JSS
361
392
  ### - :managed_password_sha256
362
393
  ### - :managed_password_length # for random generating pws
363
394
  ###
395
+ ### TODO: make individial getters/setters as for @files_processes
364
396
  attr_reader :management_account
365
397
 
366
398
  ### @return [Array<Hash>]
@@ -377,9 +409,10 @@ module JSS
377
409
  ### - :filevault_enabled => true,
378
410
  ### - :username => "chrisltest",
379
411
  ### - :password_md5 => "3858f62230ac3c915f300c664312c63f",
380
- ### - : password => "foobar",
412
+ ### - :password => "foobar",
381
413
  ### - :password_sha256=> "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"
382
414
  ###
415
+ ### TODO: make individial getters/setters as for @files_processes
383
416
  attr_reader :accounts
384
417
 
385
418
  ### @return [Array<Hash>]
@@ -414,23 +447,7 @@ module JSS
414
447
  ###
415
448
  attr_reader :scripts
416
449
 
417
-
418
- ### @return [Hash]
419
- ###
420
- ### Self-service-related data for this policy
421
- ###
422
- ### The hash keys are:
423
- ### - :self_service_icon => !{:uri => String,:id => Integer, :filename => String}
424
- ### - :use_for_self_service => true,
425
- ### - :install_button_text => "Install",
426
- ### - :self_service_description => "Descriptive text",
427
- ### - :force_users_to_view_description => false
428
- ###
429
- ### Note: we'll add a ruby-std convenience method below "self_service?"
430
- ### which returns the :use_for_self_service key.
431
- attr_reader :self_service
432
-
433
- #### user interaction
450
+ ###### user interaction
434
451
  ### These are extracted from the :user_interaction hash
435
452
  ### in the JSON output, which looks like this:
436
453
  ### :message_start => "",
@@ -438,6 +455,7 @@ module JSS
438
455
  ### :allow_deferral_until_utc => "",
439
456
  ### :message_finish => ""
440
457
  ###
458
+ ### TODO: make individial getters/setters as for @files_processes
441
459
 
442
460
  ### @return [Boolean] can the user defer the policy?
443
461
  attr_reader :user_may_defer
@@ -458,11 +476,13 @@ module JSS
458
476
  ### The hash keys are:
459
477
  ### - :user_logged_in => "Do not restart",
460
478
  ### - :minutes_until_reboot => 5,
461
- ### - :message=> "This computer will restart in 5 minutes. Please save anything you are working on and log out by choosing Log Out from the bottom of the Apple menu.",
479
+ ### - :message=> "This computer will restart in 5 minutes. yaddayadda.",
462
480
  ### - :startup_disk => "Current Startup Disk",
463
481
  ### - :specify_startup => "",
464
482
  ### - :no_user_logged_in => "Do not restart"
483
+ ### - :file_vault_2_reboot => false
465
484
  ###
485
+ ### TODO: make individial getters/setters as for @files_processes
466
486
  attr_reader :reboot_options
467
487
 
468
488
  ##### files & processes
@@ -480,7 +500,6 @@ module JSS
480
500
  ### so that the hash isn't directly changable without @need_to_update.
481
501
  ### attr_reader :files_processes
482
502
 
483
-
484
503
  ### @return [Array<Hash>]
485
504
  ###
486
505
  ### The dock items handled by this policy
@@ -506,150 +525,186 @@ module JSS
506
525
  ### Public Instance Methods
507
526
  #####################################
508
527
 
509
- ###
510
528
  ### @see APIObject#initialize
511
529
  ###
512
530
  def initialize(args = {})
513
-
514
531
  super
515
532
 
533
+ if @in_jss
534
+ gen = @init_data[:general]
535
+ @category = JSS::APIObject.get_name(gen[:category])
536
+ @frequency = gen[:frequency]
537
+ @target_drive = gen[:target_drive]
538
+ @offline = gen[:offline]
539
+ @enabled = gen[:enabled]
540
+ @site = JSS::APIObject.get_name(gen[:site][:name])
541
+ @override_default_settings = gen[:override_default_settings]
542
+ @trigger = gen[:trigger]
543
+ @trigger_events = {
544
+ trigger_startup: gen[:trigger_startup],
545
+ trigger_login: gen[:trigger_login],
546
+ trigger_logout: gen[:trigger_logout],
547
+ trigger_checkin: gen[:trigger_checkin],
548
+ trigger_network_state_changed: gen[:trigger_network_state_changed],
549
+ trigger_enrollment_complete: gen[:trigger_enrollment_complete],
550
+ trigger_other: gen[:trigger_other]
551
+ }
552
+
553
+ dtl = gen[:date_time_limitations]
554
+
555
+ @server_side_limitations = {
556
+ activation: JSS.epoch_to_time(dtl[:activation_date_epoch]),
557
+ expiration: JSS.epoch_to_time(dtl[:expiration_date_epoch])
558
+ }
559
+
560
+ @client_side_limitations = {
561
+ no_execute_on: dtl[:no_execute_on], # NOTE- there's a bug in the JSON output, it's been reported to JAMF.
562
+ no_execute_start: dtl[:no_execute_start], # String like "1:01 AM"
563
+ no_execute_end: dtl[:no_execute_end], # String like "2:02 PM"
564
+ network_requirements: gen[:network_requirements]
565
+ }
566
+
567
+ maint = @init_data[:maintenance]
568
+ @verify_startup_disk = maint[:verify]
569
+ @permissions_repair = maint[:permissions]
570
+ @recon = maint[:recon]
571
+ @fix_byhost = maint[:byhost]
572
+ @reset_name = maint[:reset_name]
573
+ @flush_system_cache = maint[:system_cache]
574
+ @install_cached_pkgs = maint[:install_all_cached_packages]
575
+ @flush_user_cache = maint[:user_cache]
576
+
577
+ amaint = @init_data[:account_maintenance]
578
+ @directory_bindings = amaint[:directory_bindings]
579
+ @open_firmware_efi_password = amaint[:open_firmware_efi_password]
580
+ @management_account = amaint[:management_account]
581
+ @accounts = amaint[:accounts]
582
+
583
+ @packages = @init_data[:package_configuration][:packages] ? @init_data[:package_configuration][:packages] : []
584
+
585
+ @scripts = @init_data[:scripts]
586
+
587
+ uint = @init_data[:user_interaction]
588
+ @user_may_defer = uint[:allow_users_to_defer]
589
+ @user_may_defer_until = JSS.parse_datetime uint[:allow_deferral_until_utc]
590
+ @user_message_start = uint[:message_start]
591
+ @user_message_finish = uint[:message_finish]
592
+
593
+ @reboot_options = @init_data[:reboot]
594
+
595
+ @files_processes = @init_data[:files_processes]
596
+
597
+ @dock_items = @init_data[:dock_items]
598
+
599
+ @disk_encryption = @init_data[:disk_encryption]
600
+
601
+ @printers = @init_data[:printers]
602
+
603
+ parse_scope
604
+ parse_self_service
605
+ @in_self_service = @init_data[:self_service][:use_for_self_service]
606
+
607
+ ### Not in jss yet
608
+ end
516
609
 
517
- gen = @init_data[:general]
518
- @category = JSS::APIObject.get_name(gen[:category])
519
- @frequency = gen[:frequency]
520
- @target_drive = gen[:target_drive]
521
- @offline = gen[:offline]
522
- @enabled = gen[:enabled]
523
- @site = JSS::APIObject.get_name(gen[:site][:name])
524
- @override_default_settings = gen[:override_default_settings]
525
- @trigger = gen[:trigger ]
526
- @trigger_events = {
527
- :trigger_startup => gen[:trigger_startup ],
528
- :trigger_login => gen[:trigger_login ],
529
- :trigger_logout => gen[:trigger_logout ],
530
- :trigger_checkin => gen[:trigger_checkin ],
531
- :trigger_network_state_changed => gen[:trigger_network_state_changed ],
532
- :trigger_enrollment_complete => gen[:trigger_enrollment_complete ],
533
- :trigger_other => gen[:trigger_other ]
534
- }
535
-
536
- dtl = gen[:date_time_limitations]
537
-
538
- @server_side_limitations = {
539
- :activation => JSS.epoch_to_time(dtl[:activation_date_epoch]),
540
- :expiration => JSS.epoch_to_time(dtl[:expiration_date_epoch])
541
- }
542
-
543
- @client_side_limitations = {
544
- :no_execute_on => dtl[:no_execute_on], # NOTE- there's a bug in the JSON output, it's been reported to JAMF.
545
- :no_execute_start => dtl[:no_execute_start], # String like "1:01 AM"
546
- :no_execute_end => dtl[:no_execute_end], # String like "2:02 PM"
547
- :network_requirements => gen[:network_requirements]
548
- }
549
-
550
- maint = @init_data[:maintenance]
551
- @verify_startup_disk = maint[:verify]
552
- @permissions_repair = maint[:permissions]
553
- @recon = maint[:recon]
554
- @fix_byhost = maint[:byhost]
555
- @reset_name = maint[:reset_name]
556
- @flush_system_cache = maint[:system_cache]
557
- @install_cached_pkgs = maint[:install_all_cached_packages]
558
- @flush_user_cache = maint[:user_cache]
559
-
560
- amaint = @init_data[:account_maintenance]
561
- @directory_bindings = amaint[:directory_bindings]
562
- @open_firmware_efi_password = amaint[:open_firmware_efi_password]
563
- @management_account = amaint[:management_account]
564
- @accounts = amaint[:accounts]
565
-
566
- @packages = @init_data[:package_configuration][:packages] ? @init_data[:package_configuration][:packages] : []
567
-
568
- @scripts = @init_data[:scripts]
569
-
570
- @self_service = @init_data[:self_service]
571
-
572
- uint = @init_data[:user_interaction]
573
- @user_may_defer = uint[:allow_users_to_defer]
574
- @user_may_defer_until = JSS.parse_datetime uint[:allow_deferral_until_utc]
575
- @user_message_start = uint[:message_start]
576
- @user_message_finish = uint[:message_finish]
577
-
578
- @reboot_options = @init_data[:reboot]
579
-
580
- @files_processes = @init_data[:files_processes]
581
-
582
- @dock_items = @init_data[:dock_items]
583
-
584
- @disk_encryption = @init_data[:disk_encryption]
585
-
586
- @printers = @init_data[:printers]
610
+ # set non-nil defaults
611
+ @enabled ||= false
612
+ @frequency ||= 'Once per computer'
613
+ @target_drive ||= '/'
614
+ @offline ||= false
615
+ @override_default_settings ||= {}
616
+ @scripts ||= []
617
+ @server_side_limitations ||= {}
618
+ @client_side_limitiations ||= {}
619
+ @trigger_events ||= {}
620
+ @directory_bindings ||= []
621
+ @open_firmware_efi_password ||= {}
622
+ @management_account ||= {}
623
+ @accounts ||= []
624
+ @packages ||= []
625
+ @scripts ||= []
626
+ @self_service ||= {}
627
+ @dock_items ||= []
628
+ @disk_encryption ||= {}
629
+ @printers ||= []
630
+ @files_processes ||= {}
631
+ unless @reboot_options
632
+ @reboot_options = {}
633
+ @reboot_options[:user_logged_in] = 'Do not restart'
634
+ @reboot_options[:no_user_logged_in] = 'Do not restart'
635
+ end
587
636
 
588
- parse_scope
637
+ @scope ||= JSS::Scopable::Scope.new(:computers, all_computers: false)
638
+ end # init
589
639
 
590
- end # init
640
+ ###### General
591
641
 
592
- ###
593
642
  ### Change the enabled state of this item
594
643
  ###
595
644
  ### @param new_val[Boolean] the new state.
596
645
  ###
597
646
  ### @return [void]
598
647
  ###
599
- def enabled= (new_val)
648
+ def enabled=(new_val)
600
649
  return nil if @enabled == new_val
601
- raise JSS::InvalidDataError, "New value must be true or false" unless JSS::TRUE_FALSE.include? new_val
650
+ raise JSS::InvalidDataError, 'New value must be true or false' unless JSS::TRUE_FALSE.include? new_val
602
651
  @enabled = new_val
603
652
  @need_to_update = true
604
653
  end
605
654
 
606
- ###
655
+ ### Shortcut for enabled = true
656
+ def enable
657
+ self.enabled = true
658
+ end
659
+
660
+ ### Shortcut for endabled = false
661
+ def disable
662
+ self.enabled = false
663
+ end
664
+
607
665
  ### Set a new frequency for this policy.
608
666
  ###
609
667
  ### @param freq[Symbol] the desired frequency, must be one of the keys of {FREQUENCIES}
610
668
  ###
611
669
  ### @return [void]
612
670
  ###
613
- def frequency= (freq)
614
- raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ", :"}" unless FREQUENCIES.keys.include? freq
671
+ def frequency=(freq)
672
+ raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ', :'}" unless FREQUENCIES.keys.include? freq
615
673
  @frequency = FREQUENCIES[freq]
616
674
  @need_to_update = true
617
675
  end
618
676
 
619
- ###
620
677
  ### Set a new target drive for this policy.
621
678
  ###
622
679
  ### @param path_to_drive[String,Pathname] the full path to the target drive, must start with a '/'
623
680
  ###
624
681
  ### @return [void]
625
682
  ###
626
- def target_drive= (path_to_drive)
627
- raise JSS::InvalidDataError, "Path to target drive must be absolute" unless path_to_drive.to_s.start_with? '/'
683
+ def target_drive=(path_to_drive)
684
+ raise JSS::InvalidDataError, 'Path to target drive must be absolute' unless path_to_drive.to_s.start_with? '/'
628
685
  @target_drive = path_to_drive.to_s
629
686
  @need_to_update = true
630
687
  end
631
688
 
632
- ###
633
689
  ### Set whether this policy is available offline.
634
690
  ###
635
691
  ### @param new_val[Boolean]
636
692
  ###
637
693
  ### @return [void]
638
694
  ###
639
- def offline= (new_val)
640
- raise JSS::InvalidDataError, "New value must be boolean true or false" unless JSS::TRUE_FALSE.include? new_val
695
+ def offline=(new_val)
696
+ raise JSS::InvalidDataError, 'New value must be boolean true or false' unless JSS::TRUE_FALSE.include? new_val
641
697
  @offline = new_val
642
698
  @need_to_update = true
643
699
  end
644
700
 
645
- ###
646
- ### Change the category of this item, arg is a category name
701
+ ### Change the category of this policy, arg is a category name
647
702
  ###
648
703
  ### @param new_val[String] the name of the new category
649
704
  ###
650
705
  ### @return [void]
651
706
  ###
652
- def category= (new_val = JSS::Category::DEFAULT_CATEGORY)
707
+ def category=(new_val = JSS::Category::DEFAULT_CATEGORY)
653
708
  return nil if @category == new_val
654
709
  new_val = nil if new_val == ''
655
710
  new_val ||= JSS::Category::DEFAULT_CATEGORY
@@ -658,7 +713,6 @@ module JSS
658
713
  @need_to_update = true
659
714
  end
660
715
 
661
- ###
662
716
  ### Change a trigger event
663
717
  ###
664
718
  ### @param type[Symbol] the type of trigger, one of the keys of {TRIGGER_EVENTS}
@@ -667,67 +721,66 @@ module JSS
667
721
  ###
668
722
  ### @return [void]
669
723
  ###
670
- def set_trigger_event (type, new_val)
724
+ def set_trigger_event(type, new_val)
671
725
  raise JSS::InvalidDataError, "Trigger type must be one of #{TRIGGER_EVENTS.keys.join(', ')}" unless TRIGGER_EVENTS.keys.include? type
672
726
  if type == :custom
673
- raise JSS::InvalidDataError, "Custom triggers must be Strings" unless new_val.kind_of? String
727
+ raise JSS::InvalidDataError, 'Custom triggers must be Strings' unless new_val.is_a? String
674
728
  else
675
- raise JSS::InvalidDataError, "Non-custom triggers must be true or false" unless JSS::TRUE_FALSE.include? new_val
729
+ raise JSS::InvalidDataError, 'Non-custom triggers must be true or false' unless JSS::TRUE_FALSE.include? new_val
676
730
  end
677
731
  @trigger_events[TRIGGER_EVENTS[type]] = new_val
678
732
  @need_to_update = true
679
733
  end
680
734
 
681
- ###
735
+ ###### Files & Processes
736
+
682
737
  ### @return [String] The unix shell command to run on ths client.
683
738
  ###
684
- def run_command ; @files_processes[:run_command] ; end
739
+ def run_command
740
+ @files_processes[:run_command]
741
+ end
685
742
 
686
- ###
687
743
  ### Set the unix shell command to be run on the client
688
744
  ###
689
745
  ### @param command[String] the unix shell command to be run on the client
690
746
  ###
691
747
  ### @return [void]
692
748
  ###
693
- def run_command= (command)
694
- raise JSS::InvalidDataError, "Command to run must be a String" unless command.is_a? String
749
+ def run_command=(command)
750
+ raise JSS::InvalidDataError, 'Command to run must be a String' unless command.is_a? String
695
751
  @files_processes[:run_command] = command
696
752
  @need_to_update = true
697
753
  end
698
754
 
699
- ###
700
755
  ### @return [Boolean] Should we update the database used by the locate command?
701
756
  ###
702
- def update_locate_database? ; @files_processes[:update_locate_database] ; end
757
+ def update_locate_database?
758
+ @files_processes[:update_locate_database]
759
+ end
703
760
 
704
- ###
705
761
  ### Set whether or not to update the database used by the locate command.
706
762
  ###
707
763
  ### @param tf[Boolean] whether or not to update the database used by the locate command.
708
764
  ###
709
765
  ### @return [void]
710
766
  ###
711
- def update_locate_database= (tf)
767
+ def update_locate_database=(tf)
712
768
  @files_processes[:update_locate_database] = tf ? true : false
713
769
  @need_to_update = true
714
770
  end
715
771
 
716
- ###
717
772
  ### @return [String] The process name to search for on the client
718
773
  ###
719
774
  def search_for_process
720
775
  @files_processes[:search_for_process]
721
776
  end
722
777
 
723
- ###
724
778
  ### @return [Boolean] Should the searched-for process be killed if found.
725
779
  ###
726
780
  def kill_process?
727
781
  @files_processes[:kill_process]
728
782
  end
729
783
 
730
- ###
731
784
  ### Set the process name to search for, and if it should be killed if found.
732
785
  ###
733
786
  ### Setter methods (which end with =) can't easily take
@@ -740,23 +793,24 @@ module JSS
740
793
  ###
741
794
  ### @return [void]
742
795
  ###
743
- def set_search_for_process (process, kill = false)
796
+ def set_search_for_process(process, kill = false)
744
797
  @files_processes[:search_for_process] = process.to_s
745
798
  @files_processes[:kill_process] = kill ? true : false
746
799
  @need_to_update = true
747
800
  end
748
801
 
749
- ###
750
802
  ### @return [Pathname] The path to search for
751
803
  ###
752
- def search_by_path ; Pathname.new @files_processes[:search_by_path] ; end
804
+ def search_by_path
805
+ Pathname.new @files_processes[:search_by_path]
806
+ end
753
807
 
754
- ###
755
808
  ### @return [Boolean] Should the searched-for path be deleted if found?
756
809
  ###
757
- def delete_file? ; @files_processes[:delete_file] ; end
810
+ def delete_file?
811
+ @files_processes[:delete_file]
812
+ end
758
813
 
759
- ###
760
814
  ### Set the path to search for, a String or Pathname, and whether or not to delete it if found.
761
815
  ###
762
816
  ### Setter methods (which end with =) can't easily take
@@ -769,17 +823,18 @@ module JSS
769
823
  ###
770
824
  ### @return [void]
771
825
  ###
772
- def set_search_by_path (path, delete = false)
773
- raise JSS::InvalidDataError, "Path to search for must be a String or a Pathname" unless path.is_a? String or path.is_a? Pathname
826
+ def set_search_by_path(path, delete = false)
827
+ raise JSS::InvalidDataError, 'Path to search for must be a String or a Pathname' unless path.is_a?(String) || path.is_a?(Pathname)
774
828
  @files_processes[:search_by_path] = path.to_s
775
829
  @files_processes[:delete_file] = delete ? true : false
776
830
  @need_to_update = true
777
831
  end
778
832
 
779
- ###
780
833
  ### @return [String] The term to search for using spotlight
781
834
  ###
782
- def spotlight_search ; @files_processes[:spotlight_search] ; end
835
+ def spotlight_search
836
+ @files_processes[:spotlight_search]
837
+ end
783
838
 
784
839
  ### Set the term to seach for using spotlight
785
840
  ###
@@ -787,16 +842,17 @@ module JSS
787
842
  ###
788
843
  ### @return [void]
789
844
  ###
790
- def spotlight_search= (term)
791
- raise JSS::InvalidDataError, "Spotlight search term must be a String" unless term.is_a? String
845
+ def spotlight_search=(term)
846
+ raise JSS::InvalidDataError, 'Spotlight search term must be a String' unless term.is_a? String
792
847
  @files_processes[:spotlight_search] = term
793
848
  @need_to_update = true
794
849
  end
795
850
 
796
- ###
797
851
  ### @return [String] The term to seach for using the locate command
798
852
  ###
799
- def locate_file ; @files_processes[:locate_file] ; end
853
+ def locate_file
854
+ @files_processes[:locate_file]
855
+ end
800
856
 
801
857
  ### Set the term to seach for using the locate command
802
858
  ###
@@ -804,68 +860,333 @@ module JSS
804
860
  ###
805
861
  ### @return [void]
806
862
  ###
807
- def locate_file= (term)
808
- raise JSS::InvalidDataError, "Term to locate must be a String" unless term.is_a? String
863
+ def locate_file=(term)
864
+ raise JSS::InvalidDataError, 'Term to locate must be a String' unless term.is_a? String
809
865
  @files_processes[:locate_file] = term
810
866
  @need_to_update = true
811
867
  end
812
868
 
869
+ ###### Client maintenance
870
+
871
+ ### Set the
872
+
873
+ ###### Packages
874
+
813
875
  ### @return [Array] the id's of the packages handled by the policy
814
- def package_ids; @packages.map{|p| p[:id]} ; end
876
+ def package_ids
877
+ @packages.map { |p| p[:id] }
878
+ end
815
879
 
816
880
  ### @return [Array] the names of the packages handled by the policy
817
- def package_names; @packages.map{|p| p[:name]} ; end
881
+ def package_names
882
+ @packages.map { |p| p[:name] }
883
+ end
884
+
885
+ ### Add a package to the list of pkgs handled by this policy.
886
+ ### If the pkg already exists in the policy, nil is returned and
887
+ ### no changes are made.
888
+ ###
889
+ ### @param [String,Integer] identifier the name or id of the package to add to this policy
890
+ ###
891
+ ### @param [Hash] opts the options for this pkg
892
+ ###
893
+ ### @option [Symbol, Integer] position: :end where to add this pkg among the list of
894
+ ### pkgs. Zero-based, :start and 0 are the same, as are :end and -1. Defaults to :end
895
+ ###
896
+ ### @option [String] action: 'Install' One of the values of PACKAGE_ACTIONS
897
+ ###
898
+ ### @option [Boolean] feu: false Overrides the setting for the pkg itself
899
+ ###
900
+ ### @option [Boolean] fut: false Overrides the setting for the pkg itself
901
+ ###
902
+ ### @option [Boolean] update_autorun: false
903
+ ###
904
+ ### @return [Array, nil] the new @packages array, nil if pkg was already in the policy
905
+ ###
906
+ def add_package(identifier, opts = {})
907
+ opts[:position] ||= -1
908
+ opts[:action] ||= :install
909
+ opts[:feu] = false if opts[:feu].nil?
910
+ opts[:fut] = false if opts[:fut].nil?
911
+ opts[:update_autorun] = false if opts[:update_autorun].nil?
912
+
913
+ id = JSS::Package.valid_id identifier
914
+ raise JSS::NoSuchItemError, "No package matches '#{identifier}'" unless id
915
+
916
+ return nil if @packages.map { |p| p[:id] }.include? id
917
+
918
+ name = JSS::Package.map_all_ids_to(:name)[id]
919
+
920
+ position = case opts[:position]
921
+ when :start then 0
922
+ when :end then -1
923
+ else opts[:position]
924
+ end
925
+
926
+ raise JSS::InvalidDataError, "action must be one of: :#{PACKAGE_ACTIONS.keys.join ', :'}" unless \
927
+ PACKAGE_ACTIONS.include? opts[:action]
928
+ raise JSS::InvalidDataError, 'feu must be true or false' unless \
929
+ JSS::TRUE_FALSE.include? opts[:feu]
930
+ raise JSS::InvalidDataError, 'fut must be true or false' unless \
931
+ JSS::TRUE_FALSE.include? opts[:fut]
932
+ raise JSS::InvalidDataError, 'update_autorun must be true or false' unless \
933
+ JSS::TRUE_FALSE.include? opts[:update_autorun]
934
+
935
+ pkg_data = {
936
+ id: id,
937
+ name: name,
938
+ action: PACKAGE_ACTIONS[opts[:action]],
939
+ feu: opts[:feu],
940
+ fut: opts[:feu],
941
+ update_autorun: opts[:update_autorun]
942
+ }
943
+
944
+ @packages.insert position, pkg_data
945
+
946
+ ### if the user gave a large number for position, it created nil entries in the array, they need
947
+ ### to be removed.
948
+ @packages.compact!
949
+
950
+ @need_to_update = true
951
+ @packages
952
+ end
953
+
954
+ ### Remove a package from this policy by name or id
955
+ ###
956
+ ### @param identfier [String,Integer] the name or id of the package to remove
957
+ ###
958
+ ### @return [Array, nil] the new packages array or nil if no change
959
+ ###
960
+ def remove_package(identifier)
961
+ removed = @packages.delete_if { |p| p[:id] == identifier || p[:name] == identifier }
962
+ @need_to_update = true if removed
963
+ removed
964
+ end
965
+
966
+ ###### Scripts
818
967
 
819
968
  ### @return [Array] the id's of the scripts handled by the policy
820
- def script_ids; @scripts.map{|p| p[:id]} ; end
969
+ def script_ids
970
+ @scripts.map { |p| p[:id] }
971
+ end
821
972
 
822
973
  ### @return [Array] the names of the scripts handled by the policy
823
- def script_names; @scripts.map{|p| p[:name]} ; end
974
+ def script_names
975
+ @scripts.map { |p| p[:name] }
976
+ end
977
+
978
+ ### Add a script to the list of SCRIPT_PRIORITIESipts run by this policy.
979
+ ### If the script already exists in the policy, nil is returned and
980
+ ### no changes are made.
981
+ ###
982
+ ### @param [String,Integer] identifier the name or id of the script to add to this policy
983
+ ###
984
+ ### @param [Hash] opts the options for this script
985
+ ###
986
+ ### @option [Symbol, Integer] position: where to add this script among the list of
987
+ ### scripts. Zero-based, :start and 0 are the same, as are :end and -1. Defaults to :end
988
+ ###
989
+ ### @option [Symbol] priority: either :before or :after
990
+ ###
991
+ ### @option [String] parameter4: the value of the 4th parameter passed to the script. this
992
+ ### overrides the same parameter in the script object itself.
993
+ ###
994
+ ### @option [String] parameter5: the value of the 5th parameter passed to the script. this
995
+ ### overrides the same parameter in the script object itself.
996
+ ###
997
+ ### @option [String] parameter6: the value of the 6th parameter passed to the script. this
998
+ ### overrides the same parameter in the script object itself.
999
+ ###
1000
+ ### @option [String] parameter7: the value of the 7th parameter passed to the script. this
1001
+ ### overrides the same parameter in the script object itself.
1002
+ ###
1003
+ ### @option [String] parameter8: the value of the 8th parameter passed to the script. this
1004
+ ### overrides the same parameter in the script object itself.
1005
+ ###
1006
+ ### @option [String] parameter9: the value of the 9th parameter passed to the script. this
1007
+ ### overrides the same parameter in the script object itself.
1008
+ ###
1009
+ ### @option [String] parameter10: the value of the 10th parameter passed to the script. this
1010
+ ### overrides the same parameter in the script object itself.
1011
+ ###
1012
+ ### @option [String] parameter11: the value of the 11th parameter passed to the script. this
1013
+ ### overrides the same parameter in the script object itself.
1014
+ ###
1015
+ ### @return [Array, nil] the new @scripts array, nil if script was already in the policy
1016
+ ###
1017
+ def add_script(identifier, opts = {})
1018
+ opts[:position] ||= -1
1019
+ opts[:priority] ||= :after
1020
+
1021
+ raise JSS::NoSuchItemError, "No script matches '#{identifier}'" unless (id = JSS::Script.valid_id(identifier))
1022
+
1023
+ return nil if @scripts.map { |s| s[:id] }.include? id
1024
+
1025
+ name = JSS::Script.map_all_ids_to(:name)[id]
1026
+
1027
+ position = case opts[:position]
1028
+ when :start then 0
1029
+ when :end then -1
1030
+ else opts[:position]
1031
+ end
1032
+
1033
+ raise JSS::InvalidDataError, "priority must be one of: :#{SCRIPT_PRIORITIES.keys.join ', :'}" unless \
1034
+ SCRIPT_PRIORITIES.include? opts[:priority]
1035
+
1036
+ script_data = {
1037
+ id: id,
1038
+ name: name,
1039
+ priority: SCRIPT_PRIORITIES[opts[:priority]],
1040
+ parameter4: opts[:parameter4],
1041
+ parameter5: opts[:parameter5],
1042
+ parameter6: opts[:parameter6],
1043
+ parameter7: opts[:parameter7],
1044
+ parameter8: opts[:parameter8],
1045
+ parameter9: opts[:parameter9],
1046
+ parameter10: opts[:parameter10],
1047
+ parameter11: opts[:parameter11]
1048
+ }
1049
+
1050
+ @scripts.insert position, script_data
1051
+
1052
+ ### if the user gave a large number for position, it created nil entries in the array, they need
1053
+ ### to be removed.
1054
+ @scripts.compact!
1055
+
1056
+ @need_to_update = true
1057
+ @scripts
1058
+ end
1059
+
1060
+ ### Remove a script from this policy by name or id
1061
+ ###
1062
+ ### @param identfier [String,Integer] the name or id of the script to remove
1063
+ ###
1064
+ ### @return [Array, nil] the new scripts array or nil if no change
1065
+ ###
1066
+ def remove_script(identifier)
1067
+ removed = @scripts.delete_if { |s| s[:id] == identifier || s[:name] == identifier }
1068
+ @need_to_update = true if removed
1069
+ removed
1070
+ end
1071
+
1072
+ ###### Directory Bindings
824
1073
 
825
1074
  ### @return [Array] the id's of the directory_bindings handled by the policy
826
- def directory_binding_ids; @directory_bindings.map{|p| p[:id]} ; end
1075
+ def directory_binding_ids
1076
+ @directory_bindings.map { |p| p[:id] }
1077
+ end
827
1078
 
828
1079
  ### @return [Array] the names of the directory_bindings handled by the policy
829
- def directory_binding_names; @directory_bindings.map{|p| p[:name]} ; end
1080
+ def directory_binding_names
1081
+ @directory_bindings.map { |p| p[:name] }
1082
+ end
1083
+
1084
+ ###### Dock items
830
1085
 
831
1086
  ### @return [Array] the id's of the dock_items handled by the policy
832
- def dock_item_ids; @dock_items.map{|p| p[:id]} ; end
1087
+ def dock_item_ids
1088
+ @dock_items.map { |p| p[:id] }
1089
+ end
833
1090
 
834
1091
  ### @return [Array] the names of the dock_items handled by the policy
835
- def dock_item_names; @dock_items.map{|p| p[:name]} ; end
1092
+ def dock_item_names
1093
+ @dock_items.map { |p| p[:name] }
1094
+ end
1095
+
1096
+ ###### Printers
836
1097
 
837
1098
  ### @return [Array] the id's of the printers handled by the policy
838
- def printer_ids; @printers.map{|p| p[:id]} ; end
1099
+ def printer_ids
1100
+ @printers.map { |p| p[:id] }
1101
+ end
839
1102
 
840
1103
  ### @return [Array] the names of the printers handled by the policy
841
- def printer_names; @printers.map{|p| p[:name]} ; end
1104
+ def printer_names
1105
+ @printers.map { |p| p[:name] }
1106
+ end
1107
+
1108
+ ###### SelfService
1109
+ ### Is this policy in self service?
1110
+ ###
1111
+ ### @return [Boolean] Is this policy in self service?
1112
+ ###
1113
+ def in_self_service?
1114
+ @in_self_service
1115
+ end
1116
+
1117
+ ### policies in self service aren't 'removable'
1118
+ ###
1119
+ ### @return [FalseClass]
1120
+ ###
1121
+ def user_removable?
1122
+ false
1123
+ end
1124
+ alias user_removable= user_removable?
1125
+
1126
+ ### Add this policy to Self Service
1127
+ ###
1128
+ ### @return [void]
1129
+ def add_to_self_service
1130
+ @need_to_update = (@in_self_service == false)
1131
+ @in_self_service = true
1132
+ end
1133
+
1134
+ ### Remove this policy from Self Service
1135
+ ###
1136
+ ### @return [void]
1137
+ def remove_from_self_service
1138
+ @need_to_update = (@in_self_service == true)
1139
+ @in_self_service = false
1140
+ end
1141
+
1142
+ ### Select or upload an image file to be the self service icon.
1143
+ ### The policy must already exist in the JSS to upload an image.
1144
+ ###
1145
+ ### NOTE: There is no way to verify the validity of an icon id, as they are not
1146
+ ### available via the API. Caveat Emptor.
1147
+ ###
1148
+ ### NOTE: When setting by id, {#update} or #{save} must be used to save the change.
1149
+ ###
1150
+ ### @param icon [String, Pathname, Integer] the path to the local image file, to upload
1151
+ ### or the id of a previously uploaded one.
1152
+ ###
1153
+ ### @return [void]
1154
+ ###
1155
+ def assign_icon(icon)
1156
+ if icon.is_a? Integer
1157
+ @self_service_icon = { id: icon }
1158
+ @need_to_update = true
1159
+ return
1160
+ end
1161
+ upload(:icon, icon)
1162
+ end
1163
+
1164
+ ###### Actions
842
1165
 
843
- ### @return [Boolean] is this policy available in SelfService?
844
- def self_service?; @self_service[:use_for_self_service] ; end
845
-
846
1166
  ### Try to execute this policy on this machine.
847
1167
  ###
848
- ### @param show_output[Boolean] should the stdout and stderr of the
1168
+ ### @param show_output[Boolean] should the stdout and stderr of the
849
1169
  ### 'jamf policy' command be sent to stdout in realtime?
850
1170
  ###
851
1171
  ### @return [Boolean, nil] The success of the 'jamf policy' command, or nil
852
1172
  ### if the policy couldn't be executed (out of scope, policy disabled, etc)
853
1173
  ###
854
- def run (show_output = false)
1174
+ def run(show_output = false)
855
1175
  return nil unless enabled?
856
- output = JSS::Client.run_jamf("policy", "-id #{id}", show_output)
1176
+ output = JSS::Client.run_jamf('policy', "-id #{id}", show_output)
857
1177
  return nil if output.include? 'No policies were found for the ID'
858
- return $?.exitstatus == 0 ? true : false
1178
+ $CHILD_STATUS.exitstatus.zero? ? true : false
859
1179
  end
860
-
861
- ### Aliases
1180
+
1181
+ ###### Aliases
1182
+
862
1183
  alias enabled? enabled
863
1184
  alias pkgs packages
864
1185
  alias command_to_run run_command
865
1186
  alias delete_path? delete_file?
866
1187
  alias execute run
867
-
868
- #####################################
1188
+ alias self_service? in_self_service?
1189
+
869
1190
  ### Private Instance Methods
870
1191
  #####################################
871
1192
  private
@@ -874,25 +1195,43 @@ module JSS
874
1195
  doc = REXML::Document.new APIConnection::XML_HEADER
875
1196
  obj = doc.add_element RSRC_OBJECT_KEY.to_s
876
1197
 
877
- general = obj.add_element "general"
1198
+ general = obj.add_element 'general'
878
1199
  general.add_element('name').text = @name
879
1200
  general.add_element('enabled').text = @enabled
880
1201
  general.add_element('frequency').text = @frequency
881
1202
  general.add_element('target_drive').text = @target_drive
882
1203
  general.add_element('offline').text = @offline
883
- general.add_element('category').add_element('name').text = @category
1204
+ general.add_element('category').add_element('name').text = @category if @category
884
1205
 
885
- JSS.hash_to_rexml_array(@trigger_events).each{|t| general << t}
1206
+ JSS.hash_to_rexml_array(@trigger_events).each { |t| general << t }
886
1207
 
887
1208
  obj << @scope.scope_xml
888
1209
 
889
- files_processes = obj.add_element "files_processes"
890
- JSS.hash_to_rexml_array(@files_processes).each{|f| files_processes << f}
1210
+ files_processes = obj.add_element 'files_processes'
1211
+ JSS.hash_to_rexml_array(@files_processes).each { |f| files_processes << f }
891
1212
 
892
- return doc.to_s
1213
+ pkg_conf = obj.add_element 'package_configuration'
1214
+ pkgs = pkg_conf.add_element 'packages'
1215
+ @packages.each do |p|
1216
+ pkg = pkgs.add_element 'package'
1217
+ pdeets = JSS.hash_to_rexml_array p
1218
+ pdeets.each { |d| pkg << d }
1219
+ end
1220
+
1221
+ scripts = obj.add_element 'scripts'
1222
+ @scripts.each do |s|
1223
+ script = scripts.add_element 'script'
1224
+ sdeets = JSS.hash_to_rexml_array s
1225
+ sdeets.each { |d| script << d }
1226
+ end
1227
+
1228
+ self_svc = self_service_xml
1229
+ self_svc.add_element('use_for_self_service').text = @in_self_service
1230
+ obj << self_svc
1231
+
1232
+ doc.to_s
893
1233
  end
894
1234
 
895
1235
  end # class policy
896
1236
 
897
1237
  end # module
898
-