xolo-server 1.0.0 → 2.0.2

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +42 -4
  3. data/bin/xoloserver +3 -0
  4. data/data/client/xolo +1233 -0
  5. data/lib/optimist_with_insert_blanks.rb +1216 -0
  6. data/lib/xolo/core/base_classes/configuration.rb +238 -0
  7. data/lib/xolo/core/base_classes/server_object.rb +112 -0
  8. data/lib/xolo/core/base_classes/title.rb +884 -0
  9. data/lib/xolo/core/base_classes/version.rb +641 -0
  10. data/lib/xolo/core/constants.rb +85 -0
  11. data/lib/xolo/core/exceptions.rb +52 -0
  12. data/lib/xolo/core/json_wrappers.rb +43 -0
  13. data/lib/xolo/core/loading.rb +59 -0
  14. data/lib/xolo/core/output.rb +292 -0
  15. data/lib/xolo/core/security_cmd.rb +128 -0
  16. data/lib/xolo/core/version.rb +21 -0
  17. data/lib/xolo/core.rb +47 -0
  18. data/lib/xolo/server/app.rb +7 -0
  19. data/lib/xolo/server/configuration.rb +243 -38
  20. data/lib/xolo/server/constants.rb +10 -0
  21. data/lib/xolo/server/helpers/auth.rb +19 -2
  22. data/lib/xolo/server/helpers/autopkg.rb +157 -0
  23. data/lib/xolo/server/helpers/client_data.rb +90 -60
  24. data/lib/xolo/server/helpers/file_transfers.rb +412 -82
  25. data/lib/xolo/server/helpers/jamf_pro.rb +31 -7
  26. data/lib/xolo/server/helpers/log.rb +2 -0
  27. data/lib/xolo/server/helpers/maintenance.rb +1 -0
  28. data/lib/xolo/server/helpers/notification.rb +4 -3
  29. data/lib/xolo/server/helpers/pkg_signing.rb +16 -12
  30. data/lib/xolo/server/helpers/progress_streaming.rb +9 -12
  31. data/lib/xolo/server/helpers/subscriptions.rb +119 -0
  32. data/lib/xolo/server/helpers/titles.rb +27 -3
  33. data/lib/xolo/server/helpers/versions.rb +23 -11
  34. data/lib/xolo/server/mixins/changelog.rb +9 -16
  35. data/lib/xolo/server/mixins/title_jamf_access.rb +375 -390
  36. data/lib/xolo/server/mixins/title_ted_access.rb +50 -8
  37. data/lib/xolo/server/mixins/version_jamf_access.rb +118 -129
  38. data/lib/xolo/server/mixins/version_ted_access.rb +34 -4
  39. data/lib/xolo/server/object_locks.rb +2 -1
  40. data/lib/xolo/server/routes/auth.rb +2 -2
  41. data/lib/xolo/server/routes/jamf_pro.rb +11 -1
  42. data/lib/xolo/server/routes/maint.rb +2 -1
  43. data/lib/xolo/server/routes/subscriptions.rb +126 -0
  44. data/lib/xolo/server/routes/title_editor.rb +1 -1
  45. data/lib/xolo/server/routes/titles.rb +26 -11
  46. data/lib/xolo/server/routes/uploads.rb +0 -14
  47. data/lib/xolo/server/routes/versions.rb +14 -13
  48. data/lib/xolo/server/routes.rb +15 -23
  49. data/lib/xolo/server/title.rb +100 -77
  50. data/lib/xolo/server/version.rb +178 -18
  51. data/lib/xolo/server.rb +8 -0
  52. metadata +20 -11
@@ -72,38 +72,11 @@ module Xolo
72
72
  # on the xolo server.
73
73
  UNINSTALL_SCRIPT_FILENAME = 'uninstall-script'
74
74
 
75
- # In the TitleEditor, the version script is
76
- # stored as an Extension Attribute - each title can
77
- # only have one.
78
- # and it needs a 'key', which is the name used to indicate the
79
- # EA in various criteria, and is the EA name in Jamf Patch.
80
- # The key is this value as a prefix on the title
81
- # so for title 'foobar', it is 'xolo-foobar'
82
- # That value is also used as the display name
83
- TITLE_EDITOR_EA_KEY_PREFIX = Xolo::Server::JAMF_OBJECT_NAME_PFX
84
-
85
- # The EA from the title editor, which is used in Jamf Patch
86
- # cannot, unfortunately, be used as a criterion in normal
87
- # smart groups or advanced searches.
88
- # Since we need a smart group containing all macs with any
89
- # version of the title installed, we need a second copy of the
90
- # EA as a 'normal' EA.
91
- #
92
- # (That group is used as an exclusion to any auto-install initial-
93
- # install policies, so that those policies don't stomp on the matching
94
- # Patch Policies)
95
- #
96
- # The 'duplicate' EA is named the same as the Titled Editor key
97
- # (see TITLE_EDITOR_EA_KEY_PREFIX) with this suffix added.
98
- # So for the Title Editor key 'xolo-<title>', we'll also have
99
- # a matching normal EA called 'xolo-<title>-installed-version'
100
- JAMF_NORMAL_EA_NAME_SUFFIX = '-installed-version'
101
-
102
- JAMF_INSTALLED_GROUP_NAME_SUFFIX = '-installed'
103
- JAMF_FROZEN_GROUP_NAME_SUFFIX = '-frozen'
104
-
105
- JAMF_UNINSTALL_SUFFIX = '-uninstall'
106
- JAMF_EXPIRE_SUFFIX = '-expire'
75
+ JAMF_INSTALLED_GROUP_NAME_SUFFIX = 'installed'
76
+ JAMF_FROZEN_GROUP_NAME_SUFFIX = 'frozen'
77
+ JAMF_MANUAL_INSTALL_RELEASED_POL_SUFFIX = 'install'
78
+ JAMF_UNINSTALL_SUFFIX = 'uninstall'
79
+ JAMF_EXPIRE_SUFFIX = 'expire'
107
80
 
108
81
  # the expire policy will run this client command,
109
82
  # appending the title
@@ -164,20 +137,6 @@ module Xolo
164
137
  title_dirs.map(&:basename).map(&:to_s)
165
138
  end
166
139
 
167
- # @return [String] The key and display name of a version script stored
168
- # in the title editor as the ExtAttr for a given title
169
- #####################
170
- def self.ted_ea_key(title)
171
- "#{TITLE_EDITOR_EA_KEY_PREFIX}#{title}"
172
- end
173
-
174
- # @return [String] The display name of a version script as a normal
175
- # EA in Jamf, which can be used in Smart Groups and Adv Searches.
176
- #####################
177
- def self.jamf_normal_ea_name(title)
178
- "#{ted_ea_key(title)}#{JAMF_NORMAL_EA_NAME_SUFFIX}"
179
- end
180
-
181
140
  # The title dir for a given title on the server,
182
141
  # which may or may not exist.
183
142
  #
@@ -228,7 +187,8 @@ module Xolo
228
187
  # for the given title
229
188
  #
230
189
  # NOTE: All instantiation should happen using the #instantiate_title method
231
- # in the server app instance. Please don't call this method directly
190
+ # in the server app instance, or related methods like all_title_objects.
191
+ # Please don't call this method directly
232
192
  #
233
193
  # @pararm title [String] the title we care about
234
194
  # @return [Xolo::Server::Title] load an existing title
@@ -239,6 +199,10 @@ module Xolo
239
199
  new parse_json(title_data_file(title).read)
240
200
  end
241
201
 
202
+ # Does this title exist in the title editor.
203
+ # WARNING: Being in the title editor doesn't necessarily mean that this title
204
+ # is managed
205
+ #
242
206
  # @param title [String] the title we are looking for
243
207
  # @pararm cnx [Windoo::Connection] The Title Editor connection to use
244
208
  # @return [Boolean] Does the given title exist in the Title Editor?
@@ -325,9 +289,6 @@ module Xolo
325
289
  # @return [Xolo::Server::App] our Sinatra server app
326
290
  attr_accessor :server_app_instance
327
291
 
328
- # @return [Integer] The Windoo::SoftwareTitle#softwareTitleId
329
- attr_accessor :ted_id_number
330
-
331
292
  # when applying updates, the new data from xadm is stored
332
293
  # here so it can be accessed by update-methods
333
294
  # and compared to the current instance values
@@ -363,6 +324,13 @@ module Xolo
363
324
  # version being released
364
325
  attr_accessor :releasing_version
365
326
 
327
+ # @return [String] The jamf ID of the patch source for this title
328
+ # if the title is subscribed. Managed titles are all in the Title Editor source.
329
+ attr_accessor :jamf_patch_source_id
330
+
331
+ # @return [String] The prefix for all jamf objects for this title, which is 'xolo-<title>' or 'xolotest-<title>' for test server
332
+ attr_reader :jamf_obj_name_pfx
333
+
366
334
  # version_order is defined in ATTRIBUTES
367
335
  alias versions version_order
368
336
 
@@ -376,19 +344,24 @@ module Xolo
376
344
  def initialize(data_hash)
377
345
  super
378
346
 
379
- @ted_id_number ||= data_hash[:ted_id_number]
380
- @jamf_patch_title_id ||= data_hash[:jamf_patch_title_id]
347
+ # ted_id_number and jamf_patch_title_id are now defined in parent classes ATTRIBUTES so are set by super
348
+
381
349
  @version_order ||= []
350
+
382
351
  @new_data_for_update = {}
383
352
  @changes_for_update = {}
384
- @jamf_installed_group_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}#{JAMF_INSTALLED_GROUP_NAME_SUFFIX}"
385
- @jamf_frozen_group_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}#{JAMF_FROZEN_GROUP_NAME_SUFFIX}"
386
353
 
387
- @jamf_manual_install_released_policy_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}-install"
354
+ @jamf_obj_name_pfx = "#{jamf_obj_name_pfx_base}#{data_hash[:title]}"
355
+ @jamf_installed_group_name = "#{jamf_obj_name_pfx}-#{JAMF_INSTALLED_GROUP_NAME_SUFFIX}"
356
+ @jamf_frozen_group_name = "#{jamf_obj_name_pfx}-#{JAMF_FROZEN_GROUP_NAME_SUFFIX}"
357
+
358
+ @jamf_manual_install_released_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_MANUAL_INSTALL_RELEASED_POL_SUFFIX}"
359
+
360
+ @jamf_uninstall_script_name = "#{jamf_obj_name_pfx}-#{JAMF_UNINSTALL_SUFFIX}"
361
+ @jamf_uninstall_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_UNINSTALL_SUFFIX}"
362
+ @jamf_expire_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_EXPIRE_SUFFIX}"
388
363
 
389
- @jamf_uninstall_script_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}#{JAMF_UNINSTALL_SUFFIX}"
390
- @jamf_uninstall_policy_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}#{JAMF_UNINSTALL_SUFFIX}"
391
- @jamf_expire_policy_name = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{data_hash[:title]}#{JAMF_EXPIRE_SUFFIX}"
364
+ # DO NOT USE jamf_cnx here, it comes from the server app instance, which is not set until after initialization.
392
365
  end
393
366
 
394
367
  # Instance Methods
@@ -402,10 +375,12 @@ module Xolo
402
375
  # @session ||= {}
403
376
  end
404
377
 
378
+ # This can be manually set earlier in the request handling to use a non-standard
379
+ # admin username
405
380
  # @return [String]
406
381
  ###################
407
382
  def admin
408
- session[:admin]
383
+ @admin ||= session[:admin]
409
384
  end
410
385
 
411
386
  # @return [Boolean] Are we creating this title?
@@ -438,6 +413,22 @@ module Xolo
438
413
  current_action == :releasing
439
414
  end
440
415
 
416
+ # TODO: Remove this when everything has been repaired for v2
417
+ # and all json files know this value
418
+ #######################
419
+ def jamf_patch_title_id
420
+ return @jamf_patch_title_id if @jamf_patch_title_id
421
+
422
+ log_debug "Getting jamf_patch_title_id for title '#{title}'"
423
+
424
+ self.jamf_patch_title_id =
425
+ if managed?
426
+ jamf_active_managed_titles[title]
427
+ else
428
+ jamf_active_subscribed_titles[title]
429
+ end
430
+ end
431
+
441
432
  # Append a message to the progress stream file,
442
433
  # optionally sending it also to the log
443
434
  #
@@ -448,8 +439,8 @@ module Xolo
448
439
  #
449
440
  # @return [void]
450
441
  ###################
451
- def progress(msg, log: :debug)
452
- server_app_instance.progress msg, log: log
442
+ def progress(msg, log: :debug, alert: false)
443
+ server_app_instance.progress msg, log: log, alert: alert
453
444
  end
454
445
 
455
446
  # @return [Windoo::Connection] a single Title Editor connection to use for
@@ -589,13 +580,6 @@ module Xolo
589
580
  template_file.read
590
581
  end
591
582
 
592
- # @return [String] The display name of a version script as a normal
593
- # EA in Jamf, which can be used in Smart Groups and Adv Searches.
594
- #####################
595
- def jamf_normal_ea_name
596
- @jamf_normal_ea_name ||= self.class.jamf_normal_ea_name title
597
- end
598
-
599
583
  # prepend a new version to the version_order
600
584
  #
601
585
  # @param version [String] the version to prepend
@@ -603,11 +587,8 @@ module Xolo
603
587
  # @return [void]
604
588
  ########################
605
589
  def prepend_version(version)
606
- lock
607
590
  version_order.unshift version
608
591
  save_local_data
609
- ensure
610
- unlock
611
592
  end
612
593
 
613
594
  # remove a version from the version_order
@@ -670,8 +651,13 @@ module Xolo
670
651
  self.created_by = admin
671
652
  log_debug "creation_date: #{creation_date}, created_by: #{created_by}"
672
653
 
654
+ log_debug "TitleData at #create: #{to_h}"
655
+
673
656
  # this will create the title as needed in the Title Editor
657
+ log_debug "Display Name before creating in ted: #{display_name}"
674
658
  create_title_in_ted
659
+
660
+ log_debug "Display Name before creating in jamf: #{display_name}"
675
661
  create_title_in_jamf
676
662
 
677
663
  # save to file last, because saving to TitleEd and Jamf will
@@ -679,6 +665,16 @@ module Xolo
679
665
  progress 'Saving title data to Xolo server'
680
666
  save_local_data
681
667
 
668
+ if subscribed?
669
+ # create version for latest available
670
+ # - either autopkg or notification to upload.
671
+ # See also Helpers::Subscriptions.process_patch_title_updated_webhook
672
+ Xolo::Server::Version.add_version_via_subscription(
673
+ title_object: self,
674
+ new_version: patch_versions(version: :latest)[0][:version]
675
+ )
676
+ end # if subscribed
677
+
682
678
  log_change msg: 'Title Created'
683
679
 
684
680
  # ssvc icon is uploaded in a separate process, and the
@@ -776,6 +772,9 @@ module Xolo
776
772
  # @return [void]
777
773
  ##########################
778
774
  def save_local_data
775
+ # If we don't have a patch source id yet, get it now
776
+ self.jamf_patch_source_id ||= Jamf::PatchSource.valid_id(patch_source, cnx: jamf_cnx) if patch_source
777
+
779
778
  # create the dirs for the title
780
779
  title_dir.mkpath
781
780
  vdir = title_dir + Xolo::Server::Version::VERSIONS_DIRNAME
@@ -838,6 +837,17 @@ module Xolo
838
837
  self.uninstall_script &&= Xolo::ITEM_UPLOADED
839
838
  end
840
839
 
840
+ # Is AutoPkg integration enabled for the server and title?
841
+ # This overrides the method in core title, which just checks for the presence of the recipe and dir.
842
+ ###############################
843
+ def autopkg_enabled?
844
+ return @autopkg_enabled if defined?(@autopkg_enabled)
845
+
846
+ @autopkg_enabled = server_app_instance.autopkg_enabled? && \
847
+ autopkg_recipe && \
848
+ autopkg_dir
849
+ end
850
+
841
851
  # are we uninstallable?
842
852
  #
843
853
  # @return [Boolean]
@@ -908,10 +918,10 @@ module Xolo
908
918
  version_objects.reverse.each do |vers|
909
919
  # vers might be nil if it was already deleted
910
920
  # e.g. a prev. attempt to delete the title failed partway through
911
- vers&.delete update_title: false
921
+ vers&.delete update_title: false, deleting_title: true
912
922
  end
913
923
 
914
- delete_title_from_ted
924
+ delete_title_from_ted unless subscribed?
915
925
 
916
926
  delete_title_from_jamf
917
927
 
@@ -940,6 +950,9 @@ module Xolo
940
950
 
941
951
  update_versions_for_release version_to_release
942
952
 
953
+ # the jamf 'manual install released' policy for the new release
954
+ # is updated in the release_version method below
955
+
943
956
  # update the title
944
957
  self.released_version = version_to_release
945
958
  save_local_data
@@ -1008,8 +1021,14 @@ module Xolo
1008
1021
  progress msg, log: :info
1009
1022
 
1010
1023
  pol = jamf_manual_install_released_policy
1011
- pol.package_ids.each { |pid| pol.remove_package pid }
1012
- pol.add_package vobj.jamf_pkg_id
1024
+ toggle_jamf_manual_install_released_policy pol, vobj
1025
+
1026
+ if self_service?
1027
+ add_title_to_self_service(pol)
1028
+ else
1029
+ remove_title_from_self_service(pol)
1030
+ end
1031
+
1013
1032
  pol.save
1014
1033
  end
1015
1034
 
@@ -1058,7 +1077,6 @@ module Xolo
1058
1077
  #
1059
1078
  # Then look at the various Jamf objects pertaining to this title, and ensure they are correct
1060
1079
  # - Accept Patch EA
1061
- # - Normal EA 'xolo-<title>-installed-version'
1062
1080
  # - title-installed smart group 'xolo-<title>-installed'
1063
1081
  # - frozen static group 'xolo-<title>-frozen'
1064
1082
  # - manual/SSvc install-current-release policy 'xolo-<title>-install'
@@ -1085,6 +1103,7 @@ module Xolo
1085
1103
  progress "Starting repair of title '#{title}'"
1086
1104
  repair_ted_title
1087
1105
  repair_jamf_title_objects
1106
+ save_local_data
1088
1107
  return unless repair_versions
1089
1108
 
1090
1109
  version_objects.each do |vobj|
@@ -1114,7 +1133,7 @@ module Xolo
1114
1133
 
1115
1134
  exp = Time.now + Xolo::Server::ObjectLocks::OBJECT_LOCK_LIMIT
1116
1135
  Xolo::Server.object_locks[title][:expires] = exp
1117
- log_debug "Locked title '#{title}' for updates until #{exp}"
1136
+ log_debug "Locked title '#{title}' for updates until #{exp}, by method #{caller_locations.first.label}"
1118
1137
  end
1119
1138
 
1120
1139
  # Unlock this v for updates
@@ -1131,7 +1150,11 @@ module Xolo
1131
1150
  ###########################
1132
1151
  def to_h
1133
1152
  hash = super
1153
+
1154
+ # TODO: remove these after 'repairing' everything for v2
1134
1155
  hash[:ted_id_number] = ted_id_number
1156
+ hash[:jamf_patch_title_id] = jamf_patch_title_id
1157
+
1135
1158
  hash[:ssvc_icon_id] = ssvc_icon_id
1136
1159
  hash
1137
1160
  end
@@ -223,12 +223,86 @@ module Xolo
223
223
  }
224
224
  end
225
225
 
226
+ # add a new version in response to a patch title update webhook event.
227
+ # This doesn't upload a pkg - it just creates the version in Xolo, and then
228
+ # someone can upload a pkg to it via xadm or autopkg will do it if configured.
229
+ #
230
+ # @param title_object [Xolo::Server::Title] the title object for the subscribed title
231
+ # @param new_version [String] the new version to add
232
+ # @return [void]
233
+ ######################
234
+ def self.add_version_via_subscription(title_object:, new_version:)
235
+ title_object.log_info "Adding new version '#{new_version}' for subscribed title '#{title_object.title}'"
236
+
237
+ # get more details about this version from the JPAPI
238
+ patch_version_data = title_object.patch_versions(version: new_version).first
239
+ unless patch_version_data
240
+ msg = "Could not get patch version data from JPAPI for version '#{new_version}' of subscribed title '#{title_object.title}'. Cannot create new version in Xolo without this data. Aborting."
241
+ title_object.log_error msg, alert: true
242
+ return
243
+ end
244
+
245
+ title_object.log_debug "Got patch version data from JPAPI for version '#{new_version}': #{patch_version_data}"
246
+
247
+ # put the data into a hash for creating a new version object
248
+ vobj_data = {
249
+ publish_date: Time.parse(patch_version_data[:releaseDate]),
250
+ standalone: patch_version_data[:standalone],
251
+ min_os: patch_version_data[:minimumOperatingSystem],
252
+ reboot: patch_version_data[:rebootRequired],
253
+ killapps: []
254
+ }
255
+
256
+ # Killapps for subscribed titles? The API only shows app names without the .app, e.g.
257
+ # "killApps": [
258
+ # {
259
+ # "appName": "ChrislTestHelper"
260
+ # },
261
+ # {
262
+ # "appName": "Chrisl Test"
263
+ # }
264
+ # ]
265
+ # Since we don't manage them, we'll just record them in the data like this...
266
+ unless patch_version_data[:killApps].pix_empty?
267
+ patch_version_data[:killApps].each do |ka|
268
+ vobj_data[:killapps] << "#{ka[:appName]}.app;unknown.from.subscription"
269
+ end
270
+ end
271
+
272
+ # instantiate the version object
273
+ title_object.log_debug "Instantiating version via subscription '#{new_version}' of title '#{title_object.title}' (#{title_object.class}) with data: #{vobj_data}"
274
+
275
+ vobj = title_object.server_app_instance.instantiate_version(
276
+ title: title_object,
277
+ version: new_version,
278
+ **vobj_data
279
+ )
280
+
281
+ # create it in xolo
282
+ vobj.create
283
+
284
+ # tell someone
285
+ msg = "ACTION REQUIRED: New pilot version '#{new_version}' for subscribed title '#{title_object.title}' has been created in Xolo via subscription."
286
+
287
+ # if not autopkg enabled, we need to tell someone to upload a pkg for this new version
288
+ unless title_object.autopkg_enabled?
289
+ # update general alert msg
290
+ msg = "#{msg}\nPlease upload a .pkg for it ASAP using this command:\n xadm edit-version #{title_object.title} #{new_version} --pkg-to-upload /path/to/installer.pkg"
291
+
292
+ # email to title contact
293
+ vobj.server_app_instance.send_email to: title_object.contact_email, subject: 'Need manual upload of xolo pkg', msg: msg
294
+ end # if title_object.autopkg_enabled?
295
+
296
+ # send general alert
297
+ vobj.log_info msg, alert: true
298
+ end
299
+
226
300
  # Attributes
227
301
  ######################
228
302
  ######################
229
303
 
230
304
  # The instance of Xolo::Server::App that instantiated this
231
- # title object. This is how we access things that are available in routes
305
+ # version object. This is how we access things that are available in routes
232
306
  # and helpers, like the single Jamf and TEd
233
307
  # connections for this App instance.
234
308
  attr_accessor :server_app_instance
@@ -297,6 +371,9 @@ module Xolo
297
371
  # one of :creating, :updating, :deleting
298
372
  attr_accessor :current_action
299
373
 
374
+ # @return [Boolean] is the pkg being processed now from an autopkg recipe?
375
+ attr_accessor :pkg_is_from_autopkg
376
+
300
377
  # Constructor
301
378
  ######################
302
379
  ######################
@@ -314,15 +391,15 @@ module Xolo
314
391
  @jamf_pkg_id ||= data_hash[:jamf_pkg_id]
315
392
 
316
393
  # and these can be generated now
317
- @jamf_obj_name_pfx = "#{Xolo::Server::JAMF_OBJECT_NAME_PFX}#{title}-#{version}"
394
+ @jamf_obj_name_pfx = "#{jamf_obj_name_pfx_base}#{title}-#{version}"
318
395
 
319
396
  @jamf_pkg_name ||= @jamf_obj_name_pfx
320
397
 
321
- @jamf_installed_group_name = "#{jamf_obj_name_pfx}#{JAMF_SMART_GROUP_NAME_INSTALLED_SFX}"
398
+ @jamf_installed_group_name = "#{jamf_obj_name_pfx}-#{JAMF_SMART_GROUP_NAME_INSTALLED_SFX}"
322
399
 
323
- @jamf_auto_install_policy_name = "#{jamf_obj_name_pfx}#{JAMF_POLICY_NAME_AUTO_INSTALL_SFX}"
324
- @jamf_manual_install_policy_name = "#{jamf_obj_name_pfx}#{JAMF_POLICY_NAME_MANUAL_INSTALL_SFX}"
325
- @jamf_auto_reinstall_policy_name = "#{jamf_obj_name_pfx}#{JAMF_POLICY_NAME_AUTO_REINSTALL_SFX}"
400
+ @jamf_auto_install_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_AUTO_INSTALL_SFX}"
401
+ @jamf_manual_install_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_MANUAL_INSTALL_SFX}"
402
+ @jamf_auto_reinstall_policy_name = "#{jamf_obj_name_pfx}-#{JAMF_POLICY_NAME_AUTO_REINSTALL_SFX}"
326
403
 
327
404
  @jamf_patch_policy_name = @jamf_obj_name_pfx
328
405
 
@@ -391,7 +468,17 @@ module Xolo
391
468
  def pilot_groups_to_use
392
469
  return @pilot_groups_to_use if @pilot_groups_to_use
393
470
 
471
+ # any defined in the version override any in the title
394
472
  @pilot_groups_to_use = changes_for_update&.key?(:pilot_groups) ? changes_for_update[:pilot_groups][:new] : pilot_groups
473
+ return @pilot_groups_to_use unless @pilot_groups_to_use.empty?
474
+
475
+ # if none defined in the version, look in the title
476
+ @pilot_groups_to_use =
477
+ if title_object.changes_for_update&.key?(:pilot_groups)
478
+ title_object.changes_for_update[:pilot_groups][:new]
479
+ else
480
+ title_object.pilot_groups
481
+ end
395
482
  end
396
483
 
397
484
  # The scope excluded groups to use in policies and patch policies for all versions of
@@ -456,10 +543,12 @@ module Xolo
456
543
  # @session ||= {}
457
544
  end
458
545
 
546
+ # This can be manually set earlier in the request handling to use a non-standard
547
+ # admin username
459
548
  # @return [String]
460
549
  ###################
461
550
  def admin
462
- session[:admin]
551
+ @admin ||= session[:admin]
463
552
  end
464
553
 
465
554
  # Append a message to the progress stream file,
@@ -472,8 +561,8 @@ module Xolo
472
561
  #
473
562
  # @return [void]
474
563
  ###################
475
- def progress(msg, log: :debug)
476
- server_app_instance.progress msg, log: log
564
+ def progress(msg, log: :debug, alert: false)
565
+ server_app_instance.progress msg, log: log, alert: alert
477
566
  end
478
567
 
479
568
  # This might have been set already if we were instantiated via our title
@@ -559,9 +648,7 @@ module Xolo
559
648
  progress 'Saving version data to Xolo server'
560
649
  save_local_data
561
650
 
562
- create_patch_in_ted
563
- enable_ted_patch
564
- title_object.enable_ted_title
651
+ create_patch_in_ted unless subscribed?
565
652
 
566
653
  create_in_jamf
567
654
 
@@ -578,10 +665,68 @@ module Xolo
578
665
  log_change msg: 'Version Created'
579
666
 
580
667
  progress "Version '#{version}' of Title '#{title}' has been created in Xolo.", log: :info
668
+
669
+ # all done unless we need to get a pkg via autopkg
670
+ # pkg upload from xadm will happen in a separate process,
671
+ # so we don't want to do it here in the create method
672
+
673
+ # do we have an uploaded pkg?
674
+ if pkg_to_upload.to_s.start_with? '/'
675
+ progress "Pkg will be uploaded to xolo via xadm shortly, from path '#{pkg_to_upload}'", log: :info
676
+
677
+ # if we have an autopkg recipe and dir, get the .pkg and upload it to Jamf
678
+ elsif title_object.autopkg_enabled?
679
+ handle_autopkg_during_create
680
+
681
+ # otherwise tell someone we need a .pkg
682
+ else
683
+ msg = "No --pkg-to-upload given for version '#{version}' of title #{title}, and no autopkg recipe enabled. Please upload a pkg via xadm or enable autopkg for this title."
684
+
685
+ # no alert when subscribed because a better alert mesg is sent from add_version_via_subscription
686
+ subscribed? ? progress(msg, log: :warn) : progress(msg, log: :warn, alert: true)
687
+ end
581
688
  ensure
582
689
  unlock
583
690
  end
584
691
 
692
+ # Do autopkg stuff during creation
693
+ #
694
+ ############################
695
+ def handle_autopkg_during_create
696
+ return unless title_object.autopkg_enabled?
697
+
698
+ pkg_src = title_object.run_autopkg_recipe
699
+
700
+ if pkg_src.nil?
701
+ msg = 'AutoPkg recipe is enabled for this title, but no pkg was found after running the recipe. Please check the AutoPkg recipe and the server log for details.'
702
+ progress msg, log: :warn, alert: true
703
+ return
704
+ end
705
+
706
+ oldest_allowed = Time.now - 1200 # 20 minutes ago
707
+ if pkg_src.mtime < oldest_allowed
708
+ msg = "AutoPkg recipe is enabled for this title, and a pkg was found after running the recipe, but it was last modified at #{pkg_src.mtime}, which is more than 20 minutes ago. To avoid accidentally uploading an old pkg, the server will not upload this pkg. Please check the AutoPkg recipe and the server log for details."
709
+ progress msg, log: :warn, alert: true
710
+ return
711
+ end
712
+
713
+ # this lets future code know that the pkg we're working with came from autopkg
714
+ self.pkg_is_from_autopkg = true
715
+
716
+ # Upload the pkg to Jamf, and associate it with this version
717
+ server_app_instance.process_and_upload_autopkg_pkg(title, self, pkg_src)
718
+ end
719
+
720
+ # Is this version part of a subscribed title?
721
+ def subscribed?
722
+ title_object.subscribed?
723
+ end
724
+
725
+ # or a managed title?
726
+ def managed?
727
+ !subscribed?
728
+ end
729
+
585
730
  # Update a this version, updating to the
586
731
  # local filesystem, Jamf Pro, and the Title Editor as needed
587
732
  #
@@ -608,7 +753,6 @@ module Xolo
608
753
 
609
754
  # update ted before jamf
610
755
  update_patch_in_ted
611
- enable_ted_patch
612
756
  update_version_in_jamf
613
757
  update_local_instance_values
614
758
  save_local_data
@@ -669,13 +813,16 @@ module Xolo
669
813
  progress "Fixing original upload date: #{new_date}, by: #{new_by}", log: :debug
670
814
  self.upload_date = new_date
671
815
  self.uploaded_by = new_by
672
- save_local_data
816
+
673
817
  end
818
+ save_local_data
674
819
  ensure
675
820
  unlock
676
821
  end
677
822
 
678
823
  # Release this version, possibly rolling back from a previously newer version
824
+ # This should only be called by the title. The initial 'release' action starts in the title,
825
+ # and then calls this method on the version to do the version-specific release steps.
679
826
  #
680
827
  # @param rollback [Boolean] If true, this version is being released as a rollback
681
828
  #
@@ -689,6 +836,7 @@ module Xolo
689
836
  progress msg, log: :info
690
837
  pol = jamf_auto_install_policy
691
838
  set_policy_release_groups pol
839
+ pol.enable
692
840
  pol.save
693
841
 
694
842
  # set scope targets of patch policy to all (in patch pols, 'all' means 'all eligible')
@@ -825,15 +973,25 @@ module Xolo
825
973
  # know the version is gone. Set this to false when the title itself
826
974
  # is being deleted and calling this method.
827
975
  #
976
+ # @param deleting_title [Boolean] Is the title itself being deleted?
977
+ #
828
978
  # @return [void]
829
979
  ##########################
830
- def delete(update_title: true)
980
+ def delete(update_title: true, deleting_title: false)
831
981
  lock
832
982
  @current_action = :deleting
833
983
 
834
- delete_patch_from_ted
835
984
  delete_version_from_jamf
836
985
 
986
+ # NOTE: we no longer delete the patch from the Title Editor
987
+ # unless the whole title is being deleted, because
988
+ # patches may be needed for reporting purposes.
989
+ # When the title is deleted, the title's delete method
990
+ # will delete all patches for all versions.
991
+ # If other situations arise where we need to delete
992
+ # ted patches individually, set deleting_title to true.
993
+ delete_patch_from_ted if deleting_title && managed?
994
+
837
995
  # remove from the title's list of versions
838
996
  progress 'Deleting version from title data on the Xolo server', log: :debug
839
997
  title_object.remove_version(version) if update_title
@@ -860,14 +1018,16 @@ module Xolo
860
1018
  raise Xolo::ServerError, 'Server is shutting down' if Xolo::Server.shutting_down?
861
1019
 
862
1020
  while locked?
863
- log_debug "Waiting for update lock on Version '#{version}' of title '#{title}'..." if (Time.now.to_i % 5).zero?
1021
+ if (Time.now.to_i % 5).zero?
1022
+ log_debug "Method #{caller_locations.first.label} is waiting for update lock on Version '#{version}' of title '#{title}'..."
1023
+ end
864
1024
  sleep 0.33
865
1025
  end
866
1026
  Xolo::Server.object_locks[title] ||= { versions: {} }
867
1027
 
868
1028
  exp = Time.now + Xolo::Server::ObjectLocks::OBJECT_LOCK_LIMIT
869
1029
  Xolo::Server.object_locks[title][:versions][version] = exp
870
- log_debug "Locked version '#{version}' of title '#{title}' for updates until #{exp}"
1030
+ log_debug "Locked version '#{version}' of title '#{title}' for updates until #{exp}, by method #{caller_locations.first.label}"
871
1031
  end
872
1032
 
873
1033
  # Unlock this version for updates
data/lib/xolo/server.rb CHANGED
@@ -27,6 +27,7 @@ require 'base64'
27
27
  require 'resolv'
28
28
  require 'shellwords'
29
29
  require 'net/smtp'
30
+ require 'etc'
30
31
 
31
32
  # Gems
32
33
  ######
@@ -102,6 +103,11 @@ module Xolo
102
103
  ##############################
103
104
  ##############################
104
105
 
106
+ ################
107
+ def self.test_server?
108
+ Xolo::Server.config.test_server
109
+ end
110
+
105
111
  ################
106
112
  def self.start_time
107
113
  @start_time
@@ -194,6 +200,8 @@ require 'xolo/server/helpers/versions'
194
200
  require 'xolo/server/helpers/client_data'
195
201
  require 'xolo/server/helpers/file_transfers'
196
202
  require 'xolo/server/helpers/maintenance'
203
+ require 'xolo/server/helpers/subscriptions'
204
+ require 'xolo/server/helpers/autopkg'
197
205
 
198
206
  require 'xolo/server/configuration'
199
207