xolo-server 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0fcb07a6a96bb7c1994ddc769614f3c88839cdad9815b5943623a49977ba038
4
- data.tar.gz: 569ff85e1c62df06bd00019a8a075b91c1a6e405fb3c6a8fb54cb49f9aaee3ef
3
+ metadata.gz: 5762b1b18f24e90927cac0c748c873b32150a55e5037d1e3db4792c93bb79e82
4
+ data.tar.gz: 5a068566e8e4c28687a26493d29a7c9506ce2247e1cd846fb1a8768d4833fdd4
5
5
  SHA512:
6
- metadata.gz: 1a333641589671b4dbd125525a2cd8c8a8ad80aa16fabe8a87faaf9ea2ca31a302931a6a91307e1cdb2ed87e2566c4f013ec4df1ab8b509574e311bae86fd9bd
7
- data.tar.gz: 795e16e01d4af3a1de5675e63aac8d9f31930d7d56b172b6fcea3b4426a51e5ad056b0a6ab70048acb61cd91eb86d2527a19b2a024154abd4366a888b65d1e5b
6
+ metadata.gz: afbc831c4e4662ccd04ef70f5cf8eef8511a65e2138b220dd74cd53efa45596b566b7dec7b3ccce0c73f9bcfbb61d444f913457ba62cb94b8a497927fcf4180d
7
+ data.tar.gz: 0cadf36f4e99a2ba8f394228bbb9cb131bd9d8b80179313528f6c4f6a8130b4581c32637646cc6cea6166d3d552ba0c44862b478ae2e0e87c5c917ab7cd0bc17
data/data/client/xolo CHANGED
@@ -54,9 +54,15 @@ JAMF='/usr/local/bin/jamf'
54
54
  # The path to the client data json file
55
55
  CLIENT_DATA_JSON_FILE="/Library/Application Support/xolo/client-data.json"
56
56
 
57
+ # The path to the test client data json file, from a text xolo server
58
+ TEST_CLIENT_DATA_JSON_FILE="/Library/Application Support/xolo/test-client-data.json"
59
+
57
60
  # The jamf policy trigger to update the client data
58
61
  UPDATE_CLIENT_DATA_TRIGGER='update-xolo-client-data'
59
62
 
63
+ # The jamf policy trigger to update the test client data
64
+ TEST_UPDATE_CLIENT_DATA_TRIGGER='test-update-xolo-client-data'
65
+
60
66
  # In the CLI args, titles and versions are separated by the first
61
67
  # occurrence of the equals sign
62
68
  TITLE_VERSION_SEPARATOR='='
@@ -141,6 +147,10 @@ Options:
141
147
  -v, --verbose: Enable verbose mode, extra information will be printed.
142
148
  -d, --debug: Enable debug mode, extra debug information will be printed.
143
149
  Implies --verbose.
150
+ -t, --test If your site has a "test" xolo server running via the same
151
+ Jamf Pro server, this causes 'xolo' to only see data, titles,
152
+ policies, etc, from test xolo server, not the production server.
153
+ Setting the XOLO_TEST_MODE env. variable does the same thing.
144
154
  -r, --recon: With 'install' or 'uninstall' run a 'jamf recon' after the
145
155
  operation completes successfully.
146
156
  -n, --no-versions: With the 'list-titles' and 'list-installed' commands,
@@ -221,6 +231,7 @@ function parse_cli() {
221
231
  {h,H,-help}=show_help \
222
232
  {v,-verbose}=be_verbose \
223
233
  {d,-debug}=debugging_on \
234
+ {t,-test}=test_mode \
224
235
  {n,-no-versions}=no_versions \
225
236
  {r,-recon}=do_recon \
226
237
  {V,-version}=show_xolo_version || \
@@ -230,10 +241,14 @@ function parse_cli() {
230
241
  show_help=$show_help[-1]
231
242
  be_verbose=$be_verbose[-1]
232
243
  debugging_on=$debugging_on[-1]
244
+ test_mode=$test_mode[-1]
233
245
  no_versions=$no_versions[-1]
234
246
  do_recon=$do_recon[-1]
235
247
  show_xolo_version=$show_xolo_version[-1]
236
248
 
249
+ # test mode can be triggered by an env var
250
+ [[ -n "$XOLO_TEST_MODE" ]] && test_mode=1
251
+
237
252
  # if debugging is on, verbose is also on
238
253
  [[ -n $debugging_on ]] && be_verbose=1
239
254
 
@@ -246,6 +261,8 @@ function parse_cli() {
246
261
  debug "..show_help is: $show_help"
247
262
  debug "..be_verbose is: $be_verbose"
248
263
  debug "..debugging_on is: $debugging_on"
264
+ debug "..test_mode is: $test_mode"
265
+ [[ -n "$XOLO_TEST_MODE" ]] && debug " ..via XOLO_TEST_MODE env."
249
266
  debug "..no_versions is: $no_versions"
250
267
  debug "..show_xolo_version is: $show_xolo_version"
251
268
 
@@ -381,7 +398,7 @@ app.includeStandardAdditions = true;
381
398
 
382
399
  // run() is automatically executed when the program is called, and will print any output returned.
383
400
  function run() {
384
- var parsed_data = JSON.parse(app.read("${CLIENT_DATA_JSON_FILE}"));
401
+ var parsed_data = JSON.parse(app.read("${client_data_json_file}"));
385
402
  var result;
386
403
 
387
404
  // the line below will sub-in whatever code we were passed
@@ -761,7 +778,7 @@ function refresh_client_data() {
761
778
 
762
779
  [[ "$command" == 'refresh' ]] && say "Refreshing client data..." || debug "Refreshing client data..."
763
780
 
764
- run_jamf_policy_trigger $UPDATE_CLIENT_DATA_TRIGGER
781
+ run_jamf_policy_trigger "$update_client_data_trigger"
765
782
 
766
783
  client_data_refreshed=1
767
784
  }
@@ -1156,6 +1173,10 @@ function expire() {
1156
1173
  ###############################
1157
1174
 
1158
1175
  function main() {
1176
+ # defaults
1177
+ client_data_json_file="$CLIENT_DATA_JSON_FILE"
1178
+ update_client_data_trigger="$UPDATE_CLIENT_DATA_TRIGGER"
1179
+
1159
1180
  # Parse the command line
1160
1181
  parse_cli "$@"
1161
1182
 
@@ -1171,10 +1192,16 @@ function main() {
1171
1192
  exit 0
1172
1193
  fi
1173
1194
 
1195
+ # use the test data file if in test mode
1196
+ if [[ -n "$test_mode" ]] ; then
1197
+ client_data_json_file="$TEST_CLIENT_DATA_JSON_FILE"
1198
+ update_client_data_trigger="$TEST_UPDATE_CLIENT_DATA_TRIGGER"
1199
+ fi
1200
+
1174
1201
  if [[ "$command" != "refresh" ]] ; then
1175
1202
  # If we're not refreshing, we need to have the client data file
1176
1203
  # and it needs to be up to date
1177
- [[ -f "$CLIENT_DATA_JSON_FILE" ]] || die "No client data file found at $CLIENT_DATA_JSON_FILE.\nPlease run the 'refresh' command to update the client data."
1204
+ [[ -f "$client_data_json_file" ]] || die "No client data file found at $client_data_json_file.\nPlease run the 'refresh' command to update the client data."
1178
1205
  fi
1179
1206
 
1180
1207
  [[ -z "$command" ]] && die "No command given.\nUsage: $USAGE\nUse --help for more information."
@@ -474,6 +474,8 @@ module Xolo
474
474
  desc: <<~ENDDESC
475
475
  One or more Jamf Computer Groups whose members will automatically have new versions installed or updated for testing before it is released.
476
476
 
477
+ These groups affect both the initial installation of and updates to a title when a version is added, before it is released.
478
+
477
479
  These computers will be used for testing not just the software, but the installation process itself. Exclusions win, so computers that are also in an excluded group for the title will not be used as pilots.
478
480
 
479
481
  Pilot groups can also be defined per version, in which case these will be ignored. Defining them in the title is useful for subscribed titles, where new versions are created automatically.
@@ -505,7 +507,9 @@ module Xolo
505
507
  readline: :jamf_computer_group_names,
506
508
  invalid_msg: 'Invalid release group(s). Must exist in Jamf and not be excluded.',
507
509
  desc: <<~ENDDESC
508
- One or more Jamf Computer Groups whose members will automatically have this title installed when new versions are released.
510
+ One or more Jamf Computer Groups whose members will automatically have this title installed.
511
+
512
+ These groups affect the _initial_ installation of a title when a version is released. Any Mac with the title installed, will get updates (if not excluded) regardless of the release-groups.
509
513
 
510
514
  If your Xolo administrators allow it, you can use '#{Xolo::TARGET_ALL}' to auto-install on all computers that aren't excluded. If not, you'll be told how to request setting release groups to '#{Xolo::TARGET_ALL}'.
511
515
 
@@ -534,7 +538,7 @@ module Xolo
534
538
  desc: <<~ENDDESC
535
539
  One or more Jamf Computer Groups whose members are not allowed to install this title.
536
540
 
537
- When a computer is in one of these groups, the title is not available even if the computer is in a pilot or release group.
541
+ When a computer is in one of these groups, the title is not available at all, for installation or updates, even if the computer is in a pilot or release group.
538
542
 
539
543
  When using the --excluded-groups CLI option, you can specify more than one group by using the option more than once, or by providing a single option value with the groups separated by commas.
540
544
 
@@ -663,6 +667,7 @@ module Xolo
663
667
  It will never be available to excluded computers.
664
668
 
665
669
  Self Service is not available for titles with the release_group 'all'.
670
+ To remove the self-service option, use --no-self-service.
666
671
  ENDDESC
667
672
  },
668
673
 
@@ -799,7 +804,7 @@ module Xolo
799
804
  # @!attribute ted_id_number
800
805
  # @return [Integer] The Windoo::SoftwareTitle#softwareTitleId
801
806
  ted_id_number: {
802
- label: 'Title Editor ID Number',
807
+ label: 'Title Editor ID',
803
808
  type: :integer,
804
809
  cli: false,
805
810
  changelog: false,
@@ -812,7 +817,7 @@ module Xolo
812
817
  # @!attribute jamf_patch_title_id
813
818
  # @return [Integer] The Windoo::SoftwareTitle#softwareTitleId
814
819
  jamf_patch_title_id: {
815
- label: 'The Jamf ID Number of this Patch Title',
820
+ label: 'Jamf Patch Title ID',
816
821
  type: :integer,
817
822
  cli: false,
818
823
  changelog: false,
@@ -170,6 +170,7 @@ module Xolo
170
170
  changelog: true,
171
171
  desc: <<~ENDDESC
172
172
  The installation of this version requires the computer to reboot. Users will be notified before installation.
173
+ To remove the reboot option, use --no-reboot.
173
174
  ENDDESC
174
175
  },
175
176
 
@@ -186,6 +187,7 @@ module Xolo
186
187
  changelog: true,
187
188
  desc: <<~ENDDESC
188
189
  The installer for this version is a full installer, not an incremental patch that must be installed on top of an earlier version.
190
+ To remove the standalone option, use --no-standalone.
189
191
  ENDDESC
190
192
  },
191
193
 
@@ -217,6 +219,7 @@ module Xolo
217
219
  use them as a killapp by specifying '#{USE_TITLE_FOR_KILLAPP}'
218
220
 
219
221
  If not using --walkthru you can use --killapps multiple times
222
+ To remove all killapps, use '#{Xolo::NONE}'.
220
223
  ENDDESC
221
224
  },
222
225
 
@@ -237,6 +240,8 @@ module Xolo
237
240
  This can cause problems if that unknown version is actually newer than this version, e.g. a beta or pre-release version, or when the app has a 'self-update' mechanism that installs newer versions outside of Jamf Patch before it is aware of them.
238
241
 
239
242
  But sometimes it may be desirable to have all unknown versions updated to this version, e.g. when the title is a helper app that is not regularly updated, or when the title is being newly managed by Xolo/Jamf Patch and you want to get all existing installations onto this version.
243
+
244
+ To remove the patch-unknown option, use --no-patch-unknown.
240
245
  ENDDESC
241
246
  },
242
247
 
@@ -262,7 +267,7 @@ module Xolo
262
267
 
263
268
  When using the --pilot-groups CLI option, you can specify more than one group by using the option more than once, or by providing a single option value with the groups separated by commas.
264
269
 
265
- When adding a new version, the pilot groups from the previous version will be inherited if you don't specify any. To make the new version have no pilot groups use '#{Xolo::NONE}'.
270
+ When adding a new version, the pilot groups from the previous version will be inherited if you don't specify any. To make the version have no pilot groups use '#{Xolo::NONE}'.
266
271
 
267
272
  NOTE: Any non-excluded computer can be used for piloting at any time by manually installing the yet-to-be-released version using `sudo xolo install <title> <version>`. The members of the pilot groups are just the ones that will have it auto-installed.
268
273
  ENDDESC
@@ -12,7 +12,7 @@ module Xolo
12
12
 
13
13
  module Version
14
14
 
15
- VERSION = '2.0.2'.freeze
15
+ VERSION = '2.0.3'.freeze
16
16
 
17
17
  end
18
18
 
@@ -92,6 +92,7 @@ module Xolo
92
92
  # the autopkg run command for this title
93
93
  #####################################
94
94
  def autopkg_run_command(title_object)
95
+ # is autopkg enabled for the server as a whole?
95
96
  return unless autopkg_enabled?
96
97
 
97
98
  [
@@ -101,10 +102,10 @@ module Xolo
101
102
  'sudo',
102
103
  '-u',
103
104
  Xolo::Server.config.autopkg_user,
104
- Xolo::Server.config.autopkg_executable.shellescape,
105
+ Xolo::Server.config.autopkg_executable,
105
106
  'run',
106
107
  '--verbose',
107
- title_object.autopkg_recipe.shellescape,
108
+ title_object.autopkg_recipe,
108
109
  FAIL_UNTRUSTED_RECIPES_CLI_OPT
109
110
  ]
110
111
  end
@@ -118,10 +119,12 @@ module Xolo
118
119
  # or nil if the recipe is not enabled for this title
119
120
  ##############################
120
121
  def run_autopkg_recipe(title_object)
122
+ # is autopkg enabled for this title?
121
123
  return unless title_object.autopkg_enabled?
122
124
 
123
125
  recipe = title_object.autopkg_recipe
124
126
  pkgdir = Pathname.new title_object.autopkg_dir
127
+ clean_old_pkgs pkgdir
125
128
 
126
129
  cmd = autopkg_run_command(title_object)
127
130
  progress "Running AutoPkg recipe for #{title_object.title} via command: #{cmd.join(' ')}", log: :info
@@ -140,14 +143,24 @@ module Xolo
140
143
  pkgs.max_by(&:mtime)
141
144
 
142
145
  else
143
- progress "ERROR: AutoPkg recipe #{autopkg_recipe} failed with status #{status.exitstatus}.", log: :error, alert: true
146
+ progress "ERROR: AutoPkg recipe #{recipe} failed with status #{status.exitstatus}.", log: :error, alert: true
144
147
  log_error 'AutoPkg output:'
145
148
  souterr.lines.each { |l| log_error "AutoPkg: #{l.chomp}" }
146
149
 
147
- raise "AutoPkg recipe #{autopkg_recipe} failed."
150
+ raise "AutoPkg recipe #{recipe} failed."
148
151
  end
149
152
  end
150
153
 
154
+ # remove any existing pkgs from the given autopkg output dir
155
+ #
156
+ # @param pkgdir[Pathname] the directory to clean
157
+ # @return [void]
158
+ ##################
159
+ def clean_old_pkgs(pkgdir)
160
+ log_debug "Removing .pkg files from #{pkgdir} before running autopkg recipe"
161
+ pkgdir.children.each { |c| c.delete if c.extname == '.pkg' }
162
+ end
163
+
151
164
  end # AutoPkg
152
165
 
153
166
  end # Helpers
@@ -19,7 +19,7 @@ module Xolo
19
19
  # This is used as a 'helper' in the Sinatra server
20
20
  #
21
21
  # This means methods here are available in all routes, views, and helpers
22
- # the Sinatra server app.
22
+ # in the Sinatra server app.
23
23
  #
24
24
  # The client data package is a Jamf::JPackage that installs a JSON file on all
25
25
  # managed Macs. This JSON file contains data about all titles and versions, and
@@ -177,7 +177,6 @@ module Xolo
177
177
  delete_jamf_uninstall_policy
178
178
  delete_jamf_manual_install_released_policy
179
179
  delete_jamf_uninstall_script
180
- delete_lingering_policies_for_title
181
180
  sleep 5
182
181
 
183
182
  # must delete this group before the patch title
@@ -192,19 +191,6 @@ module Xolo
192
191
  delete_jamf_frozen_group
193
192
  end
194
193
 
195
- ################################
196
- def delete_lingering_policies_for_title
197
- Jamf::Policy.all_names(:refresh, cnx: jamf_cnx).each do |polname|
198
- next unless polname.start_with? jamf_obj_name_pfx
199
-
200
- polid = Jamf::Policy.valid_id(polname, cnx: jamf_cnx)
201
- next unless polid
202
-
203
- progress "Jamf: Deleting lingering policy #{polname}, possibly from a failed version action.", log: :info
204
- Jamf::Policy.delete(polid, cnx: jamf_cnx)
205
- end
206
- end
207
-
208
194
  # If any title changes require updates to existing versions in
209
195
  # Jamf, this loops thru the versions and applies
210
196
  # them
@@ -281,12 +267,13 @@ module Xolo
281
267
  ######################
282
268
  def patch_report(vers: nil)
283
269
  vers = Xolo::Server::Helpers::JamfPro::PATCH_REPORT_UNKNOWN_VERSION if vers == Xolo::UNKNOWN
284
- vers &&= CGI.escape vers.to_s
285
270
 
286
271
  page_size = Xolo::Server::Helpers::JamfPro::PATCH_REPORT_JPAPI_PAGE_SIZE
287
272
  page = 0
288
- paged_rsrc = "#{patch_report_rsrc}?page=#{page}&page-size=#{page_size}"
289
- paged_rsrc << "&filter=version%3D%3D#{vers}" if vers
273
+ paged_rsrc = +"#{patch_report_rsrc}?page=#{page}&page-size=#{page_size}"
274
+
275
+ filter = vers ? jp_api_filter_string_for_version(vers) : nil
276
+ paged_rsrc << filter if filter
290
277
 
291
278
  report = []
292
279
  loop do
@@ -297,7 +284,7 @@ module Xolo
297
284
  report += data
298
285
  page += 1
299
286
  paged_rsrc = "#{patch_report_rsrc}?page=#{page}&page-size=#{page_size}"
300
- paged_rsrc << "&filter=version%3D%3D#{vers}" if vers
287
+ paged_rsrc << filter if filter
301
288
  end
302
289
 
303
290
  # log_debug "REPORT: #{report}"
@@ -310,6 +297,27 @@ module Xolo
310
297
  report
311
298
  end
312
299
 
300
+ # given a version, return a patch report filter
301
+ # with it properly quoted and URL escaped
302
+ #
303
+ # e.g. this string: 7.1.5 (84650)
304
+ # becomes: version=="7.1.5 (84650)"
305
+ # becomes: version%3D%3D%227.1.5+%2884650%29%22
306
+ # becomes: &filter=version%3D%3D%227.1.5+%2884650%29%22
307
+ #
308
+ # Use CGI.escape because it encodes all special cars, like parens,
309
+ # which we want here, rather than URI::Parser.new.escape
310
+ # which is meant to parse whole URLs leaving URL functional characters.
311
+ #
312
+ # @param vers [String] the version the filter for
313
+ # @return [String] the quoted and escaped filter string
314
+ ################################
315
+ def jp_api_filter_string_for_version(vers)
316
+ raw_fiter_query = "version==\"#{vers}\""
317
+ escaped_filter_query = CGI.escape raw_fiter_query
318
+ "&filter=#{escaped_filter_query}"
319
+ end
320
+
313
321
  ####### The Patch Ext Attribute
314
322
  ###########################################
315
323
  ###########################################
@@ -1496,6 +1504,13 @@ module Xolo
1496
1504
 
1497
1505
  configure_pol_for_self_service(pol)
1498
1506
  pol.save
1507
+
1508
+ # loop thru versions to make any SSvc changes
1509
+ version_objects.each do |vobj|
1510
+ ppol = vobj.jamf_patch_policy
1511
+ vobj.configure_patch_pol_for_self_service ppol
1512
+ ppol.save
1513
+ end
1499
1514
  end
1500
1515
 
1501
1516
  # Add the jamf_manual_install_released_policy to self service if needed
@@ -1511,6 +1526,14 @@ module Xolo
1511
1526
  pol.remove_from_self_service
1512
1527
 
1513
1528
  pol.save
1529
+
1530
+ # loop thru versions to make any SSvc changes
1531
+ version_objects.each do |vobj|
1532
+ progress "Jamf: Removing patch policy for version '#{vobj.version}' of title '#{title}' from Self Service'", log: :info
1533
+ ppol = vobj.jamf_patch_policy
1534
+ ppol.remove_from_self_service
1535
+ ppol.save
1536
+ end
1514
1537
  end
1515
1538
 
1516
1539
  # Update whether or not we are in self service, based on the setting in the title
@@ -1541,8 +1564,6 @@ module Xolo
1541
1564
  end
1542
1565
 
1543
1566
  pol.save
1544
-
1545
- # TODO: if we decide to use ssvc in patch policies, loop thru versions to make any changes
1546
1567
  end
1547
1568
 
1548
1569
  # configure the self-service settings of the manual_install_released_policy
@@ -1563,17 +1584,18 @@ module Xolo
1563
1584
  pol.add_self_service_category new_cat if new_cat
1564
1585
  end
1565
1586
 
1566
- new_display_name = changes_for_update&.dig(:self_service_display_name, :new) || display_name
1587
+ new_display_name = changes_for_update&.dig(:display_name, :new) || display_name
1567
1588
  progress "Jamf: Setting Self Service display name to '#{new_display_name}'", log: :debug
1568
1589
  pol.self_service_display_name = new_display_name
1569
1590
 
1570
- new_desc = changes_for_update&.dig(:self_service_description, :new) || description
1591
+ new_desc = changes_for_update&.dig(:description, :new) || description
1571
1592
  progress "Jamf: Setting Self Service description to '#{new_desc}'", log: :debug
1572
1593
  pol.self_service_description = new_desc
1573
1594
 
1574
1595
  pol.self_service_install_button_text = Xolo::Server::Title::SELF_SERVICE_INSTALL_BTN_TEXT
1575
1596
  return unless ssvc_icon_file
1576
1597
 
1598
+ # TODO: Is this needed if we already have a ssvc_icon_id ??
1577
1599
  progress 'Jamf: Uploading Self Service icon', log: :debug
1578
1600
  pol.save # won't do anything unless needed, but has to exist before we can upload icons
1579
1601
  pol.upload :icon, ssvc_icon_file
@@ -212,16 +212,7 @@ module Xolo
212
212
  update_jamf_pkg_reboot if changes_for_update&.key? :reboot
213
213
  update_jamf_pkg_min_os if changes_for_update&.key? :min_os
214
214
 
215
- # TODO: Update the critera for the jamf_installed_group IF
216
- #
217
- # - the group exists AND
218
- # - the title has changed how it determines installed versions, e.g. by adding or
219
- # changing a version_script or app_bundle_id
220
- # Changing those is very rare, and ill-advised, so we will implement
221
- # this later.
222
- #
223
- # if jamf_installed_group_exist?
224
- # end
215
+ update_jamf_patch_policy_patch_unknown if changes_for_update&.key? :patch_unknown
225
216
  end
226
217
 
227
218
  # Validate and fix any Jamf::JPackage objects that
@@ -411,6 +402,19 @@ module Xolo
411
402
  jamf_package.save
412
403
  end
413
404
 
405
+ # update the :patch_unknown for the patch policy
406
+ # @return [void]
407
+ ##############################
408
+ def update_jamf_patch_policy_patch_unknown
409
+ return unless changes_for_update&.key? :patch_unknown
410
+
411
+ ppol = jamf_patch_policy
412
+ action = changes_for_update[:patch_unknown][:new] ? 'Enabling' : 'Disabling'
413
+ progress "Jamf: #{action} 'patch_unknown' for patch policy #{ppol.name}", log: :debug
414
+ ppol.patch_unknown = changes_for_update[:patch_unknown][:new] ? true : false
415
+ ppol.save
416
+ end
417
+
414
418
  # repair the package object only
415
419
  #############################
416
420
  def repair_jamf_package
@@ -879,28 +883,6 @@ module Xolo
879
883
  def create_jamf_patch_policy
880
884
  progress "Jamf: Creating Patch Policy for Version '#{version}' of Title '#{title}'.", log: :info
881
885
 
882
- # TODO: decide how many patch policies - see comments at top
883
- # Probably one: for pilots initially and then rescoped to all for release
884
- #
885
- # TODO: How to set these, and should they be settable
886
- # at the Xolo::Title or Xolo::Version level?
887
- #
888
- # allow downgrade? No, to start with.
889
- # When a version is released, IF we are rolling back, then this will be set.
890
- # This is to be set only on the current release and only when it was a rollback.
891
- #
892
- # patch_unknown_versions... yes?
893
- #
894
- # if not in ssvc:
895
- # - grace period?
896
- # - update warning Message and Subject
897
- #
898
- # if in ssvc:
899
- # - any way to use existing icon?
900
- # - use title desc... do we want a version desc??
901
- # - notifications? Message and Subject? SSvc only, Notif Ctr?
902
- # - deadline and grace period message and subbject
903
-
904
886
  ppol = Jamf::PatchPolicy.create(
905
887
  cnx: jamf_cnx,
906
888
  name: jamf_patch_policy_name,
@@ -908,26 +890,12 @@ module Xolo
908
890
  target_version: version
909
891
  )
910
892
 
911
- # when first creating a patch policy, its status is always
912
- # 'pilot' so the scope targets are the pilot groups, if any.
913
- # When the version is released, the patch policy will be
914
- # rescoped to all targets (limited by eligibility)
915
- set_policy_pilot_groups ppol
893
+ configure_jamf_patch_policy(ppol)
916
894
 
917
- # exclusions are for always
918
- set_policy_exclusions ppol
919
-
920
- # This will be set to true as needed if
895
+ # This is always false to start will be set to true as needed if
921
896
  # a rollback is being done
922
897
  ppol.allow_downgrade = false
923
898
 
924
- # This should default to false, so that
925
- # we don't accidentally downgrade non-xolo test installs,
926
- # or server-pushed updates (like with commvault or cisco VPN)
927
- ppol.patch_unknown = patch_unknown ? true : false
928
-
929
- ppol.enable
930
-
931
899
  ppol.save
932
900
 
933
901
  # refetch it rather than using the one we just created
@@ -941,14 +909,28 @@ module Xolo
941
909
  assign_pkg_to_patch_in_jamf
942
910
 
943
911
  ppol = jamf_patch_policy
912
+
913
+ configure_jamf_patch_policy(ppol)
914
+
915
+ ppol.save
916
+ end
917
+
918
+ # Configure the patch policy
919
+ # NOTE: This doesn't save the changes!
920
+ ##############################
921
+ def configure_jamf_patch_policy(ppol)
944
922
  ppol.name = jamf_patch_policy_name
945
923
  ppol.target_version = version
946
924
 
947
- # This should default tofalse, so that
925
+ # This should default to false, so that
948
926
  # we don't accidentally downgrade non-xolo test installs,
949
927
  # or server-pushed updates (like with commvault or cisco VPN)
950
928
  ppol.patch_unknown = patch_unknown ? true : false
951
929
 
930
+ # when first creating a patch policy, its status is always
931
+ # 'pilot' so the scope targets are the pilot groups, if any.
932
+ # When the version is released, the patch policy will be
933
+ # rescoped to all targets (limited by eligibility)
952
934
  if pilot?
953
935
  set_policy_pilot_groups ppol
954
936
  else
@@ -958,13 +940,74 @@ module Xolo
958
940
  # exclusions are for always
959
941
  set_policy_exclusions ppol
960
942
 
943
+ # only pilot and released versions should have the patch policy enabled
961
944
  if pilot? || released?
962
945
  ppol.enable
963
946
  else
964
947
  ppol.disable
965
948
  end
966
949
 
967
- ppol.save
950
+ configure_patch_pol_for_self_service ppol
951
+ end
952
+
953
+ # Configure the patch policy for self service
954
+ # NOTE: Does NOT save the policy changes!
955
+ # @param ppol [Jamf::PatchPolicy] The patch policy to configure
956
+ # @return [void]
957
+ # ###############################
958
+ def configure_patch_pol_for_self_service(ppol = nil)
959
+ ppol ||= jamf_patch_policy
960
+
961
+ # if we're here we're in ssvc
962
+ progress "Jamf: Configuring patch policy for version '#{version}' of title '#{title}' for Self Service'", log: :info
963
+
964
+ # TODO: xadm settings for most of these ssvc values.
965
+ # TODO: deadlines, graceperiod ? needed in ruby-jss
966
+ msg = "#{title_object.display_name} is ready to be updated to version #{version}"
967
+ ppol.add_to_self_service
968
+ ppol.self_service_install_button_text = 'Update'
969
+ ppol.self_service_description = msg
970
+ ppol.self_service_notifications_enabled = true
971
+ ppol.self_service_notification_type = :ssvc_and_nctr
972
+ ppol.self_service_notification_subject = "#{title_object.display_name} update available"
973
+ ppol.self_service_notification_message = msg
974
+ ppol.self_service_reminders_enabled = true
975
+ ppol.self_service_reminder_frequency = 24 # hours
976
+
977
+ return unless title_object.ssvc_icon_id
978
+
979
+ set_patch_policy_ssvc_icon ppol, title_object.ssvc_icon_id
980
+ end
981
+
982
+ # Set an existing ssvc icon for a given patch policy
983
+ #
984
+ # NOTE: Update this if/when ruby-jss and the JPAPI can handle
985
+ # PatchPolicy Icons.
986
+ #
987
+ # This shouldn't interfere with a future #save of a PatchPolicy
988
+ # object, since that xml won't contain self_service_icon data at all.
989
+ #
990
+ # @param ppol [Jamf::PatchPolicy] The Patch Pol. to set the icon for
991
+ # @param icon_id [Integer, String] The id of the existing icon to use.
992
+ # @return [Void]
993
+ ##########################
994
+ def set_patch_policy_ssvc_icon(ppol, icon_id)
995
+ # this str can't be frozen for now
996
+ icon_xml = +<<~ENDXML
997
+ <?xml version="1.0" encoding="UTF-8"?>
998
+ <patch_policy>
999
+ <user_interaction>
1000
+ <self_service_icon>
1001
+ <id>#{icon_id}</id>
1002
+ </self_service_icon>
1003
+ </user_interaction>
1004
+ </patch_policy>
1005
+ ENDXML
1006
+
1007
+ log_debug "Jamf: Adding existing SSvc icon to patch policy for version '#{version}' of title '#{title}'"
1008
+ # TODO: in ruby-jss, update c_put and related methods to
1009
+ # use unfrozen dups of strings for #gsub
1010
+ jamf_cnx.c_put ppol.rest_rsrc, icon_xml
968
1011
  end
969
1012
 
970
1013
  # @return [String] the URL for the Jamf Pro Patch Policy that updates to this version
@@ -53,12 +53,6 @@ module Xolo
53
53
 
54
54
  with_streaming do
55
55
  title.create
56
- # we don't need to update client data when managed titles are created
57
- # because they don't have any versions yet, so there's nothing a
58
- # client can do with them.
59
- #
60
- # However subscribed titles will have their latest version activated
61
- # will need client data updated
62
56
  end
63
57
  end
64
58
 
@@ -99,7 +93,8 @@ module Xolo
99
93
  log_info "Admin #{session[:admin]} is updating title '#{params[:title]}'"
100
94
  with_streaming do
101
95
  title.update new_data
102
- update_client_data
96
+ # Moved to title.update
97
+ # update_client_data
103
98
  end
104
99
  end
105
100
 
@@ -127,7 +122,8 @@ module Xolo
127
122
 
128
123
  with_streaming do
129
124
  title.release vers_to_release
130
- update_client_data
125
+ # moved to title.release
126
+ # update_client_data
131
127
  end
132
128
  end
133
129
 
@@ -148,7 +144,8 @@ module Xolo
148
144
 
149
145
  with_streaming do
150
146
  title.repair repair_versions: do_versions
151
- update_client_data
147
+ # moved to title.repair
148
+ # update_client_data
152
149
  end
153
150
  end
154
151
 
@@ -162,7 +159,8 @@ module Xolo
162
159
 
163
160
  with_streaming do
164
161
  title.delete
165
- update_client_data
162
+ # moved to title.delete
163
+ # update_client_data
166
164
  end
167
165
  end
168
166
 
@@ -77,7 +77,8 @@ module Xolo
77
77
  Xolo::Server.rw_lock(data[:title], data[:version]).with_write_lock do
78
78
  with_streaming do
79
79
  vers.create
80
- update_client_data
80
+ # moved to vers.create
81
+ # update_client_data
81
82
  end
82
83
  end
83
84
  end
@@ -126,7 +127,8 @@ module Xolo
126
127
 
127
128
  with_streaming do
128
129
  vers.update new_data
129
- update_client_data
130
+ # moved to vers.update
131
+ # update_client_data
130
132
  end
131
133
  end
132
134
 
@@ -149,7 +151,8 @@ module Xolo
149
151
 
150
152
  with_streaming do
151
153
  vers.delete
152
- update_client_data
154
+ # moved to vers.delete
155
+ # update_client_data
153
156
  end
154
157
  end
155
158
 
@@ -206,7 +209,8 @@ module Xolo
206
209
 
207
210
  with_streaming do
208
211
  vers.repair
209
- update_client_data
212
+ # moved to vers.repair
213
+ # update_client_data
210
214
  end
211
215
  end
212
216
 
@@ -677,6 +677,10 @@ module Xolo
677
677
 
678
678
  log_change msg: 'Title Created'
679
679
 
680
+ # we don't need to update client data when titles are created
681
+ # because they don't have any versions yet, so there's nothing a
682
+ # client can do with them. It'll be updated when the version is created
683
+
680
684
  # ssvc icon is uploaded in a separate process, and the
681
685
  # title data file will be updated as needed then.
682
686
  ensure
@@ -729,7 +733,8 @@ module Xolo
729
733
 
730
734
  # any new self svc icon will be uploaded in a separate process
731
735
  # and the local data will be updated again then
732
- #
736
+
737
+ server_app_instance.update_client_data
733
738
  rescue => e
734
739
  log_change msg: "ERROR: The update failed and the changes didn't all go through!\n#{e.class}: #{e.message}\nSee server log for details."
735
740
 
@@ -929,6 +934,7 @@ module Xolo
929
934
 
930
935
  progress "Deleting Xolo server data for title '#{title}'", log: :info
931
936
  title_dir.rmtree
937
+ server_app_instance.update_client_data
932
938
  ensure
933
939
  unlock
934
940
  end
@@ -950,12 +956,10 @@ module Xolo
950
956
 
951
957
  update_versions_for_release version_to_release
952
958
 
953
- # the jamf 'manual install released' policy for the new release
954
- # is updated in the release_version method below
955
-
956
959
  # update the title
957
960
  self.released_version = version_to_release
958
961
  save_local_data
962
+ server_app_instance.update_client_data
959
963
  ensure
960
964
  unlock
961
965
  end
@@ -1110,6 +1114,8 @@ module Xolo
1110
1114
  progress '#########'
1111
1115
  vobj.repair
1112
1116
  end
1117
+
1118
+ server_app_instance.update_client_data
1113
1119
  ensure
1114
1120
  unlock
1115
1121
  end
@@ -0,0 +1,38 @@
1
+ # Temp fixes for ruby-jss
2
+ ###########################
3
+ module Jamf
4
+
5
+ #########################
6
+ module SelfServable
7
+
8
+ # set ssvc notification settings in xml
9
+ # FIX: its 'notification_enabled' NOT 'notifications_enabled'
10
+ def add_self_service_notification_xml(ssvc)
11
+ return unless @self_service_data_config[:notifications_supported]
12
+
13
+ # oldstyle/broken, only sscv notifs
14
+ if @self_service_data_config[:notifications_supported] == :ssvc_only
15
+ ssvc.add_element('notification').text = self_service_notifications_enabled.to_s
16
+ ssvc.add_element('notification_subject').text = self_service_notification_subject if self_service_notification_subject
17
+ ssvc.add_element('notification_message').text = self_service_notification_message if self_service_notification_message
18
+ return
19
+ end
20
+
21
+ # newstyle, 'notifications' subset
22
+ notif = ssvc.add_element('notifications')
23
+ # NEXT LINE IS THE FIX until pushed via ruby-jss, its 'notification_enabled' NOT 'notifications_enabled'
24
+ notif.add_element('notification_enabled').text = self_service_notifications_enabled.to_s
25
+ notif.add_element('notification_type').text = NOTIFICATION_TYPES[self_service_notification_type] if self_service_notification_type
26
+ notif.add_element('notification_subject').text = self_service_notification_subject if self_service_notification_subject
27
+ notif.add_element('notification_message').text = self_service_notification_message if self_service_notification_message
28
+
29
+ return unless @self_service_data_config[:notification_reminders]
30
+
31
+ reminds = notif.add_element('reminders')
32
+ reminds.add_element('notification_reminders_enabled').text = self_service_reminders_enabled.to_s
33
+ reminds.add_element('notification_reminder_frequency').text = self_service_reminder_frequency.to_s if self_service_reminder_frequency
34
+ end
35
+
36
+ end # module
37
+
38
+ end # module
@@ -281,8 +281,11 @@ module Xolo
281
281
  # create it in xolo
282
282
  vobj.create
283
283
 
284
+ # moved to vobj.create:
285
+ # vobj.server_app_instance.update_client_data
286
+
284
287
  # tell someone
285
- msg = "ACTION REQUIRED: New pilot version '#{new_version}' for subscribed title '#{title_object.title}' has been created in Xolo via subscription."
288
+ msg = "ACTION REQUIRED: New pilot for subscribed title '#{title_object.title}', version '#{new_version}' has been created in Xolo via subscription."
286
289
 
287
290
  # if not autopkg enabled, we need to tell someone to upload a pkg for this new version
288
291
  unless title_object.autopkg_enabled?
@@ -638,6 +641,10 @@ module Xolo
638
641
  lock
639
642
  @current_action = :creating
640
643
 
644
+ # NOTE: this for later in this method,
645
+ # since uploads are processed in separate threads
646
+ pkg_upload_given = !pkg_to_upload.pix_empty?
647
+
641
648
  self.creation_date = Time.now
642
649
  self.created_by = admin
643
650
  self.status = STATUS_PENDING
@@ -671,7 +678,8 @@ module Xolo
671
678
  # so we don't want to do it here in the create method
672
679
 
673
680
  # do we have an uploaded pkg?
674
- if pkg_to_upload.to_s.start_with? '/'
681
+ if pkg_upload_given
682
+
675
683
  progress "Pkg will be uploaded to xolo via xadm shortly, from path '#{pkg_to_upload}'", log: :info
676
684
 
677
685
  # if we have an autopkg recipe and dir, get the .pkg and upload it to Jamf
@@ -685,6 +693,8 @@ module Xolo
685
693
  # no alert when subscribed because a better alert mesg is sent from add_version_via_subscription
686
694
  subscribed? ? progress(msg, log: :warn) : progress(msg, log: :warn, alert: true)
687
695
  end
696
+
697
+ server_app_instance.update_client_data
688
698
  ensure
689
699
  unlock
690
700
  end
@@ -757,6 +767,7 @@ module Xolo
757
767
  update_local_instance_values
758
768
  save_local_data
759
769
 
770
+ server_app_instance.update_client_data
760
771
  # new pkg uploads happen in a separate process
761
772
  rescue => e
762
773
  log_change msg: "ERROR: The update failed and the changes didn't all go through!\n#{e.class}: #{e.message}\nSee server log for details."
@@ -816,6 +827,10 @@ module Xolo
816
827
 
817
828
  end
818
829
  save_local_data
830
+ # no need to update client data now if we're being repaired along with our title
831
+ return if title_object.current_action == :repairing
832
+
833
+ server_app_instance.update_client_data
819
834
  ensure
820
835
  unlock
821
836
  end
@@ -1002,6 +1017,7 @@ module Xolo
1002
1017
  log_change msg: 'Version Deleted'
1003
1018
 
1004
1019
  progress "Version '#{version}' of Title '#{title}' has been deleted from Xolo.", log: :info
1020
+ server_app_instance.update_client_data
1005
1021
  ensure
1006
1022
  unlock
1007
1023
  end
data/lib/xolo/server.rb CHANGED
@@ -43,6 +43,10 @@ require 'concurrent/atomic/reentrant_read_write_lock'
43
43
  require 'concurrent/executor/thread_pool_executor'
44
44
  require 'concurrent/timer_task'
45
45
 
46
+ # Temp fixes or overrides, esp for ruby-jss or windoo
47
+ #############
48
+ require 'xolo/server/tmp_overrides'
49
+
46
50
  # Xolo Server - order matters
47
51
  ######
48
52
  require 'xolo/server/constants'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xolo-server
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.2
4
+ version: 2.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Lasell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-16 00:00:00.000000000 Z
11
+ date: 2026-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -220,6 +220,7 @@ files:
220
220
  - lib/xolo/server/routes/uploads.rb
221
221
  - lib/xolo/server/routes/versions.rb
222
222
  - lib/xolo/server/title.rb
223
+ - lib/xolo/server/tmp_overrides.rb
223
224
  - lib/xolo/server/version.rb
224
225
  homepage: https://pixaranimationstudios.github.io/xolo-home/
225
226
  licenses: