xolo-server 1.0.1 → 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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/data/client/xolo +182 -82
  3. data/lib/xolo/core/base_classes/title.rb +261 -20
  4. data/lib/xolo/core/base_classes/version.rb +53 -8
  5. data/lib/xolo/core/constants.rb +7 -3
  6. data/lib/xolo/core/security_cmd.rb +128 -0
  7. data/lib/xolo/core/version.rb +1 -1
  8. data/lib/xolo/core.rb +1 -0
  9. data/lib/xolo/server/app.rb +7 -0
  10. data/lib/xolo/server/configuration.rb +243 -37
  11. data/lib/xolo/server/constants.rb +10 -0
  12. data/lib/xolo/server/helpers/auth.rb +19 -2
  13. data/lib/xolo/server/helpers/autopkg.rb +170 -0
  14. data/lib/xolo/server/helpers/client_data.rb +91 -61
  15. data/lib/xolo/server/helpers/file_transfers.rb +412 -82
  16. data/lib/xolo/server/helpers/jamf_pro.rb +30 -7
  17. data/lib/xolo/server/helpers/log.rb +2 -0
  18. data/lib/xolo/server/helpers/maintenance.rb +1 -0
  19. data/lib/xolo/server/helpers/notification.rb +4 -3
  20. data/lib/xolo/server/helpers/pkg_signing.rb +16 -12
  21. data/lib/xolo/server/helpers/progress_streaming.rb +9 -12
  22. data/lib/xolo/server/helpers/subscriptions.rb +119 -0
  23. data/lib/xolo/server/helpers/titles.rb +27 -3
  24. data/lib/xolo/server/helpers/versions.rb +23 -11
  25. data/lib/xolo/server/mixins/changelog.rb +9 -16
  26. data/lib/xolo/server/mixins/title_jamf_access.rb +395 -383
  27. data/lib/xolo/server/mixins/title_ted_access.rb +29 -3
  28. data/lib/xolo/server/mixins/version_jamf_access.rb +185 -159
  29. data/lib/xolo/server/mixins/version_ted_access.rb +25 -0
  30. data/lib/xolo/server/object_locks.rb +2 -1
  31. data/lib/xolo/server/routes/auth.rb +2 -2
  32. data/lib/xolo/server/routes/jamf_pro.rb +11 -1
  33. data/lib/xolo/server/routes/maint.rb +2 -1
  34. data/lib/xolo/server/routes/subscriptions.rb +126 -0
  35. data/lib/xolo/server/routes/title_editor.rb +1 -1
  36. data/lib/xolo/server/routes/titles.rb +30 -17
  37. data/lib/xolo/server/routes/uploads.rb +0 -14
  38. data/lib/xolo/server/routes/versions.rb +22 -17
  39. data/lib/xolo/server/routes.rb +9 -0
  40. data/lib/xolo/server/title.rb +107 -78
  41. data/lib/xolo/server/tmp_overrides.rb +38 -0
  42. data/lib/xolo/server/version.rb +193 -15
  43. data/lib/xolo/server.rb +12 -0
  44. metadata +8 -9
@@ -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,8 +665,22 @@ 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
 
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
+
684
684
  # ssvc icon is uploaded in a separate process, and the
685
685
  # title data file will be updated as needed then.
686
686
  ensure
@@ -733,7 +733,8 @@ module Xolo
733
733
 
734
734
  # any new self svc icon will be uploaded in a separate process
735
735
  # and the local data will be updated again then
736
- #
736
+
737
+ server_app_instance.update_client_data
737
738
  rescue => e
738
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."
739
740
 
@@ -776,6 +777,9 @@ module Xolo
776
777
  # @return [void]
777
778
  ##########################
778
779
  def save_local_data
780
+ # If we don't have a patch source id yet, get it now
781
+ self.jamf_patch_source_id ||= Jamf::PatchSource.valid_id(patch_source, cnx: jamf_cnx) if patch_source
782
+
779
783
  # create the dirs for the title
780
784
  title_dir.mkpath
781
785
  vdir = title_dir + Xolo::Server::Version::VERSIONS_DIRNAME
@@ -838,6 +842,17 @@ module Xolo
838
842
  self.uninstall_script &&= Xolo::ITEM_UPLOADED
839
843
  end
840
844
 
845
+ # Is AutoPkg integration enabled for the server and title?
846
+ # This overrides the method in core title, which just checks for the presence of the recipe and dir.
847
+ ###############################
848
+ def autopkg_enabled?
849
+ return @autopkg_enabled if defined?(@autopkg_enabled)
850
+
851
+ @autopkg_enabled = server_app_instance.autopkg_enabled? && \
852
+ autopkg_recipe && \
853
+ autopkg_dir
854
+ end
855
+
841
856
  # are we uninstallable?
842
857
  #
843
858
  # @return [Boolean]
@@ -908,10 +923,10 @@ module Xolo
908
923
  version_objects.reverse.each do |vers|
909
924
  # vers might be nil if it was already deleted
910
925
  # e.g. a prev. attempt to delete the title failed partway through
911
- vers&.delete update_title: false
926
+ vers&.delete update_title: false, deleting_title: true
912
927
  end
913
928
 
914
- delete_title_from_ted
929
+ delete_title_from_ted unless subscribed?
915
930
 
916
931
  delete_title_from_jamf
917
932
 
@@ -919,6 +934,7 @@ module Xolo
919
934
 
920
935
  progress "Deleting Xolo server data for title '#{title}'", log: :info
921
936
  title_dir.rmtree
937
+ server_app_instance.update_client_data
922
938
  ensure
923
939
  unlock
924
940
  end
@@ -943,6 +959,7 @@ module Xolo
943
959
  # update the title
944
960
  self.released_version = version_to_release
945
961
  save_local_data
962
+ server_app_instance.update_client_data
946
963
  ensure
947
964
  unlock
948
965
  end
@@ -1008,8 +1025,14 @@ module Xolo
1008
1025
  progress msg, log: :info
1009
1026
 
1010
1027
  pol = jamf_manual_install_released_policy
1011
- pol.package_ids.each { |pid| pol.remove_package pid }
1012
- pol.add_package vobj.jamf_pkg_id
1028
+ toggle_jamf_manual_install_released_policy pol, vobj
1029
+
1030
+ if self_service?
1031
+ add_title_to_self_service(pol)
1032
+ else
1033
+ remove_title_from_self_service(pol)
1034
+ end
1035
+
1013
1036
  pol.save
1014
1037
  end
1015
1038
 
@@ -1058,7 +1081,6 @@ module Xolo
1058
1081
  #
1059
1082
  # Then look at the various Jamf objects pertaining to this title, and ensure they are correct
1060
1083
  # - Accept Patch EA
1061
- # - Normal EA 'xolo-<title>-installed-version'
1062
1084
  # - title-installed smart group 'xolo-<title>-installed'
1063
1085
  # - frozen static group 'xolo-<title>-frozen'
1064
1086
  # - manual/SSvc install-current-release policy 'xolo-<title>-install'
@@ -1085,12 +1107,15 @@ module Xolo
1085
1107
  progress "Starting repair of title '#{title}'"
1086
1108
  repair_ted_title
1087
1109
  repair_jamf_title_objects
1110
+ save_local_data
1088
1111
  return unless repair_versions
1089
1112
 
1090
1113
  version_objects.each do |vobj|
1091
1114
  progress '#########'
1092
1115
  vobj.repair
1093
1116
  end
1117
+
1118
+ server_app_instance.update_client_data
1094
1119
  ensure
1095
1120
  unlock
1096
1121
  end
@@ -1114,7 +1139,7 @@ module Xolo
1114
1139
 
1115
1140
  exp = Time.now + Xolo::Server::ObjectLocks::OBJECT_LOCK_LIMIT
1116
1141
  Xolo::Server.object_locks[title][:expires] = exp
1117
- log_debug "Locked title '#{title}' for updates until #{exp}"
1142
+ log_debug "Locked title '#{title}' for updates until #{exp}, by method #{caller_locations.first.label}"
1118
1143
  end
1119
1144
 
1120
1145
  # Unlock this v for updates
@@ -1131,7 +1156,11 @@ module Xolo
1131
1156
  ###########################
1132
1157
  def to_h
1133
1158
  hash = super
1159
+
1160
+ # TODO: remove these after 'repairing' everything for v2
1134
1161
  hash[:ted_id_number] = ted_id_number
1162
+ hash[:jamf_patch_title_id] = jamf_patch_title_id
1163
+
1135
1164
  hash[:ssvc_icon_id] = ssvc_icon_id
1136
1165
  hash
1137
1166
  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