xolo-server 1.0.1 → 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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/data/client/xolo +152 -79
  3. data/lib/xolo/core/base_classes/title.rb +254 -18
  4. data/lib/xolo/core/base_classes/version.rb +47 -7
  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 +157 -0
  14. data/lib/xolo/server/helpers/client_data.rb +90 -60
  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 +375 -385
  27. data/lib/xolo/server/mixins/title_ted_access.rb +29 -3
  28. data/lib/xolo/server/mixins/version_jamf_access.rb +95 -112
  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 +26 -11
  37. data/lib/xolo/server/routes/uploads.rb +0 -14
  38. data/lib/xolo/server/routes/versions.rb +14 -13
  39. data/lib/xolo/server/routes.rb +9 -0
  40. data/lib/xolo/server/title.rb +100 -77
  41. data/lib/xolo/server/version.rb +177 -15
  42. data/lib/xolo/server.rb +8 -0
  43. metadata +7 -9
@@ -0,0 +1,126 @@
1
+ # Copyright 2025 Pixar
2
+ #
3
+ # Licensed under the terms set forth in the LICENSE.txt file available at
4
+ # at the root of this project.
5
+ #
6
+
7
+ # frozen_string_literal: true
8
+
9
+ # main module
10
+ module Xolo
11
+
12
+ # Server Module
13
+ module Server
14
+
15
+ module Routes
16
+
17
+ # See comments for Xolo::Server::Helpers::TitleEditor
18
+ #
19
+ module Subscriptions
20
+
21
+ # This is how we 'extend' modules to Sinatra servers
22
+ # for route definitions and similar things
23
+ #
24
+ # (things to be 'included' for use in route and view processing
25
+ # are mixed in by delcaring them to be helpers)
26
+ #
27
+ # We make them extentions here with
28
+ # extend Sinatra::Extension (from sinatra-contrib)
29
+ # and then 'register' them in the server with
30
+ # register Xolo::Server::<Module>
31
+ # Doing it this way allows us to split the code into a logical
32
+ # file structure, without re-opening the Sinatra::Base server app,
33
+ # and let xeitwork do the requiring of those files
34
+ extend Sinatra::Extension
35
+
36
+ # Module methods
37
+ #
38
+ ##############################
39
+ ##############################
40
+
41
+ # when this module is included
42
+ def self.included(includer)
43
+ Xolo.verbose_include includer, self
44
+ end
45
+
46
+ # when this module is extended
47
+ def self.extended(extender)
48
+ Xolo.verbose_extend extender, self
49
+ end
50
+
51
+ # Routes
52
+ #
53
+ ##############################
54
+ ##############################
55
+
56
+ # This endpoint receives Jamf Webhook PatchSoftwareTitleUpdated events
57
+ # from the Jamf Pro server, indicating that a subscribed title has a new version available.
58
+ #
59
+ # If the title is subscribed in Xolo, this creates a new xolo version for the title,
60
+ # and either notifies the contact email for the title, or uses autopkg to get an installer.
61
+ #
62
+ # The body will be JSON like this:
63
+ # {
64
+ # "event": {
65
+ # "jssID": integer,
66
+ # "lastUpdate": epoch,
67
+ # "latestVersion": "string",
68
+ # "name": "string",
69
+ # "reportUrls": [
70
+ # "string",
71
+ # "string"
72
+ # ]
73
+ # },
74
+ # "webhook": {
75
+ # "eventTimestamp": epoch,
76
+ # "id": integer,
77
+ # "name": "string",
78
+ # "webhookEvent": "PatchSoftwareTitleUpdated"
79
+ # }
80
+ # }
81
+ #
82
+ # Some real data:
83
+ # {
84
+ # "event": {
85
+ # "jssID": 145,
86
+ # "lastUpdate":1765926131000,
87
+ # "latestVersion": "1.0.3",
88
+ # "name": "Xolo Testing",
89
+ # "reportUrls": [
90
+ # "https://myjamf.mycompany.com:8443//patch.html?id=145&o=r"
91
+ # ]
92
+ # },
93
+ # "webhook": {
94
+ # "eventTimestamp":1765926428322,
95
+ # "id": 4,
96
+ # "name": "PatchSoftwareTitleUpdated",
97
+ # "webhookEvent": "PatchSoftwareTitleUpdated"
98
+ # }
99
+ # }
100
+ #
101
+ # NOTE: The above reportUrls is incorrect on modern Jamf Pro servers.
102
+ # the correct one would be https://myjamf.mycompany.com:8443/view/computers/patch/145?tab=report
103
+ #
104
+ ###############
105
+ post '/subscribed-title-updates' do
106
+ session[:admin] = Xolo::Server::WEBHOOK_HANDLER_ADMIN_USERNAME
107
+ request.body.rewind
108
+
109
+ # this happens in a thread so that we can return a 200 response to the
110
+ # webhook immediately, and do the processing asynchronously (which may
111
+ # involve time-consuming tasks like autopkg runs)
112
+ process_patch_title_updated_webhook(request.body.read)
113
+
114
+ # always return 200 to the webhook sender
115
+ status 200
116
+ resp = { status: 200, message: 'Webhook event received' }
117
+ body resp
118
+ end
119
+
120
+ end # Module
121
+
122
+ end # Routes
123
+
124
+ end # Server
125
+
126
+ end # module Xolo
@@ -18,7 +18,7 @@ module Xolo
18
18
  #
19
19
  module TitleEditor
20
20
 
21
- # This is how we 'mix in' modules to Sinatra servers
21
+ # This is how we 'extend' modules to Sinatra servers
22
22
  # for route definitions and similar things
23
23
  #
24
24
  # (things to be 'included' for use in route and view processing
@@ -16,7 +16,7 @@ module Xolo
16
16
 
17
17
  module Titles
18
18
 
19
- # This is how we 'mix in' modules to Sinatra servers
19
+ # This is how we 'extend' modules to Sinatra servers
20
20
  # for route definitions and similar things
21
21
  #
22
22
  # (things to be 'included' for use in route and view processing
@@ -48,11 +48,17 @@ module Xolo
48
48
  halt_on_existing_title title.title
49
49
 
50
50
  log_info "Admin #{session[:admin]} is creating title '#{title.title}'"
51
+ info = { status: 'created', title: title.title }
52
+ body info
53
+
51
54
  with_streaming do
52
55
  title.create
53
- # we don't need to update client data when titles are created
56
+ # we don't need to update client data when managed titles are created
54
57
  # because they don't have any versions yet, so there's nothing a
55
58
  # client can do with them.
59
+ #
60
+ # However subscribed titles will have their latest version activated
61
+ # will need client data updated
56
62
  end
57
63
  end
58
64
 
@@ -240,13 +246,14 @@ module Xolo
240
246
  #################################
241
247
  get '/titles/:title/urls' do
242
248
  log_debug "Admin #{session[:admin]} is fetching GUI URLS for title '#{params[:title]}'"
249
+
243
250
  halt_on_missing_title params[:title]
244
251
  title = instantiate_title params[:title]
245
- data = {
246
- ted_title_url: title.ted_title_url,
247
- jamf_installed_group_url: title.jamf_installed_group_url,
248
- jamf_frozen_group_url: title.jamf_frozen_group_url
249
- }
252
+
253
+ data = {}
254
+ data[:ted_title_url] = title.ted_title_url if title.managed?
255
+ data[:jamf_installed_group_url] = title.jamf_installed_group_url if title.jamf_installed_group_exist?
256
+ data[:jamf_frozen_group_url] = title.jamf_frozen_group_url if title.jamf_frozen_group_exist?
250
257
  data[:jamf_manual_install_released_policy_url] = title.jamf_manual_install_released_policy_url if title.jamf_manual_install_released_policy_exist?
251
258
 
252
259
  if title.uninstallable?
@@ -255,13 +262,11 @@ module Xolo
255
262
  data[:jamf_expire_policy_url] = title.jamf_expire_policy_url if title.expiration
256
263
  end
257
264
 
258
- if title.jamf_ted_title_active?
259
- data[:jamf_patch_title_url] = title.jamf_patch_title_url unless title.versions.empty?
265
+ if title.jamf_title_active?
266
+ data[:jamf_patch_title_url] = title.jamf_patch_title_url unless title.jamf_patch_title_id.pix_empty?
260
267
  data[:jamf_patch_ea_url] = title.jamf_patch_ea_url if title.version_script
261
268
  end
262
269
 
263
- data[:jamf_normal_ea_url] = title.jamf_normal_ea_url if title.version_script
264
-
265
270
  body data
266
271
  end
267
272
 
@@ -276,6 +281,16 @@ module Xolo
276
281
  body title.changelog
277
282
  end
278
283
 
284
+ # Does this xolo server support autopkg integration?
285
+ #
286
+ # @return [Array<Hash>] The changelog for a title
287
+ #################################
288
+ get '/titles/autopkg_enabled' do
289
+ log_debug "Admin #{session[:admin]} is checking if autopkg is enabled on this server"
290
+ data = { autopkg_enabled: autopkg_enabled? }
291
+ body data
292
+ end
293
+
279
294
  end # Titles
280
295
 
281
296
  end # Routes
@@ -62,20 +62,6 @@ module Xolo
62
62
  ##############################
63
63
  ##############################
64
64
 
65
- # # param with the uploaded file must be :file
66
- # ######################
67
- # post '/upload/ssvc-icon/:title' do
68
- # process_incoming_ssvc_icon
69
- # body({ result: :uploaded })
70
- # end
71
-
72
- # # param with the uploaded file must be :file
73
- # ######################
74
- # post '/upload/pkg/:title/:version' do
75
- # process_incoming_pkg
76
- # body({ result: :uploaded })
77
- # end
78
-
79
65
  # param with the uploaded file must be :file
80
66
  ######################
81
67
  post '/upload/test' do
@@ -16,7 +16,7 @@ module Xolo
16
16
 
17
17
  module Versions
18
18
 
19
- # This is how we 'mix in' modules to Sinatra servers
19
+ # This is how we 'extend' modules to Sinatra servers
20
20
  # for route definitions and similar things
21
21
  #
22
22
  # (things to be 'included' for use in route and view processing
@@ -58,9 +58,10 @@ module Xolo
58
58
  data[:min_os] = default_min_os if data[:min_os].pix_empty?
59
59
 
60
60
  log_debug "Incoming new version data: #{data}"
61
- log_debug "Incoming new version data: #{data.class}"
62
61
 
63
- vers = instantiate_version(data)
62
+ vers = instantiate_version(**data)
63
+ log_debug "Instantiated version object: #{vers.class} - #{vers.title} #{vers.version}"
64
+
64
65
  halt_on_existing_version vers.title, vers.version
65
66
 
66
67
  if vers.title_object.jamf_patch_ea_awaiting_acceptance? && !Xolo::Server.config.jamf_auto_accept_xolo_eas
@@ -89,9 +90,9 @@ module Xolo
89
90
 
90
91
  log_debug "Admin #{session[:admin]} is listing all versions for title '#{params[:title]}'"
91
92
  # body all_versions(params[:title])
92
- vers_ins = all_version_instances(params[:title])
93
+ vers_objs = all_version_objects(params[:title])
93
94
  # log_debug "vers_ins: #{vers_ins}"
94
- body vers_ins.map(&:to_h)
95
+ body vers_objs.map(&:to_h)
95
96
  end
96
97
 
97
98
  # get all the data for a single version
@@ -156,7 +157,7 @@ module Xolo
156
157
  # param with the uploaded file must be :file
157
158
  ######################
158
159
  post '/titles/:title/versions/:version/pkg' do
159
- process_incoming_pkg
160
+ process_and_upload_uploaded_pkg
160
161
  body({ result: :uploaded })
161
162
  end
162
163
 
@@ -239,13 +240,13 @@ module Xolo
239
240
 
240
241
  halt_on_missing_version params[:title], params[:version]
241
242
  vers = instantiate_version title: params[:title], version: params[:version]
242
- data = {
243
- ted_patch_url: vers.ted_patch_url,
244
- jamf_auto_install_policy_url: vers.jamf_auto_install_policy_url,
245
- jamf_manual_install_policy_url: vers.jamf_manual_install_policy_url,
246
- jamf_patch_policy_url: vers.jamf_patch_policy_url,
247
- jamf_package_url: vers.jamf_package_url
248
- }
243
+
244
+ data = {}
245
+ data[:ted_patch_url] = vers.ted_patch_url if vers.managed?
246
+ data[:jamf_auto_install_policy_url] = vers.jamf_auto_install_policy_url if vers.jamf_auto_install_policy_exist?
247
+ data[:jamf_manual_install_policy_url] = vers.jamf_manual_install_policy_url if vers.jamf_manual_install_policy_exist?
248
+ data[:jamf_patch_policy_url] = vers.jamf_patch_policy_url if vers.jamf_patch_policy_exist?
249
+ data[:jamf_package_url] = vers.jamf_package_url if vers.jamf_package_exist?
249
250
  data[:jamf_version_installed_group] = vers.jamf_installed_group_url if vers.jamf_installed_group_exist?
250
251
  data[:jamf_auto_reinstall_policy_url] = vers.jamf_auto_reinstall_policy_url if vers.jamf_auto_reinstall_policy_exist?
251
252
 
@@ -49,6 +49,14 @@ module Xolo
49
49
  log_debug "Checking if request path '#{request.path}' is in INTERNAL_ROUTES"
50
50
  break if Xolo::Server::Helpers::Auth::INTERNAL_ROUTES.include?(request.path) && valid_internal_auth_token?
51
51
 
52
+ # This route is used by Jamf Pro Webhooks to notify Xolo of subscribed title updates
53
+ log_debug "Checking if request path '#{request.path}' is PATCH_TITLE_UPDATED_WEBHOOK_ROUTE"
54
+ if request.path == Xolo::Server::Helpers::Auth::PATCH_TITLE_UPDATED_WEBHOOK_ROUTE
55
+ break if jamf_webhook_auth_token_ok?
56
+
57
+ halt 401, { status: 401, error: 'Valid Bearer Token Required' }
58
+ end
59
+
52
60
  # these routes are for server admins only, and require an authenticated session
53
61
  log_debug "Checking if request path '#{request.path}' is in SERVER_ADMIN_ROUTES"
54
62
  break if Xolo::Server::Helpers::Auth::SERVER_ADMIN_ROUTES.include?(request.path) && valid_server_admin?
@@ -149,3 +157,4 @@ require 'xolo/server/routes/title_editor'
149
157
  require 'xolo/server/routes/titles'
150
158
  require 'xolo/server/routes/uploads'
151
159
  require 'xolo/server/routes/versions'
160
+ require 'xolo/server/routes/subscriptions'
@@ -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