Module: Xolo::Server::Helpers::ClientData
- Defined in:
- lib/xolo/server/helpers/client_data.rb
Overview
Constants and methods for maintaining the client data package
This is used as a 'helper' in the Sinatra server
This means methods here are available in all routes, views, and helpers in the Sinatra server app.
The client data package is a Jamf::JPackage that installs a JSON file on all managed Macs. This JSON file contains data about all titles and versions, and any other data that the xolo client needs to know about.
It is updated automatically by the server when titles or versions are changed.
It is used so that the xolo client can know what it needs to know about titles and versions without having to query the server or do anything over a network other than using the jamf binary.
The downside is that the client data package is likely to be somewhat out of date, but that is a tradeoff for the simplicity and security of the client.
The client data package is installed in /Library/Application Support/xolo/client-data.json it contains a JSON object with a 'titles' key, which is an object with keys for each title. The data provided is that produced by the Title#to_h and Version#to_h methods.
Constant Summary collapse
- CLIENT_DATA_STR =
Constants
'client-data'- CLIENT_DATA_COMPONENT_PACKAGE_FILE =
The name of the package file that installs the xolo-client-data JSON file
"#{CLIENT_DATA_STR}-component.pkg"
Class Method Summary collapse
-
.client_data_mutex ⇒ Mutex
A mutex for the client data update process.
-
.extended(extender) ⇒ Object
when this module is extended.
-
.included(includer) ⇒ Object
when this module is included.
Instance Method Summary collapse
-
#build_component_client_data_pkg_file(root_dir, pkg_version, pkg_work_dir) ⇒ Pathname
Build the component install pkg with pkgbuild NOTE: no need to shellescape the paths, since we are using the array version of Open3.capture2e.
-
#build_dist_client_data_pkg_file(component_pkg_file, pkg_version, pkg_work_dir) ⇒ Pathname
Build the distribution package for the xolo-client-data JSON file NOTE: no need to shellescape the paths, since we are using the array version of Open3.capture2e.
-
#client_app_source ⇒ Pathname
The path to the client executable 'xolo' in the ruby gem.
-
#client_data_auto_policy_name ⇒ String
The name of the Jamf::Policy object that installs the xolo-client-data package automatically on all managed Macs NOTE: Set the category to Xolo::Server::JAMF_XOLO_CATEGORY.
-
#client_data_filename ⇒ String
The name of the client-data JSON file in the xolo-client-data package this is the file that is installed onto managed Macs in /Library/Application Support/xolo/.
-
#client_data_hash ⇒ Hash
The data to put in the xolo-client-data JSON file.
-
#client_data_jpackage ⇒ Jamf::JPackage
The xolo-client-data package object.
-
#client_data_manual_policy_name ⇒ String
The name of the Jamf::Policy object that installs the xolo-client-data package manually on a managed Mac.
-
#client_data_manual_policy_trigger ⇒ String
The trgger event for the manual policy to update the client data JSON file.
-
#client_data_package_file ⇒ String
The name of the package file that installs the xolo-client-data JSON file.
-
#client_data_package_identifier ⇒ String
The package identifier for the xolo-client-data package.
-
#client_data_package_name ⇒ String
The name of the Jamf Package object that contains the xolo client-data NOTE: Set the category to Xolo::Server::JAMF_XOLO_CATEGORY.
-
#client_data_testing ⇒ Object
temp.
-
#create_client_data_jamf_package ⇒ Jamf::JPackage
Create and return the xolo-client-data package in Jamf Pro.
-
#create_client_data_policies_if_needed ⇒ void
Create the xolo-client-data policies in Jamf Pro.
-
#create_client_data_policy(pol_name) ⇒ void
Create a xolo-client-data install policy in Jamf Pro.
-
#create_new_client_data_pkg_file ⇒ Pathname
Create the xolo-client-data package installer file.
-
#flush_client_data_policy_logs ⇒ void
Flush the logs for the xolo-client-data policies.
-
#update_client_data(post_maint: false) ⇒ void
update the xolo-client-data package and the policy that installs it.
Class Method Details
.client_data_mutex ⇒ Mutex
A mutex for the client data update process
TODO: use Concrrent Ruby instead of Mutex
77 78 79 |
# File 'lib/xolo/server/helpers/client_data.rb', line 77 def self.client_data_mutex @client_data_mutex ||= Mutex.new end |
.extended(extender) ⇒ Object
when this module is extended
67 68 69 |
# File 'lib/xolo/server/helpers/client_data.rb', line 67 def self.extended(extender) Xolo.verbose_extend extender, self end |
.included(includer) ⇒ Object
when this module is included
62 63 64 |
# File 'lib/xolo/server/helpers/client_data.rb', line 62 def self.included(includer) Xolo.verbose_include includer, self end |
Instance Method Details
#build_component_client_data_pkg_file(root_dir, pkg_version, pkg_work_dir) ⇒ Pathname
Build the component install pkg with pkgbuild NOTE: no need to shellescape the paths, since we are using the array version of Open3.capture2e
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
# File 'lib/xolo/server/helpers/client_data.rb', line 330 def build_component_client_data_pkg_file(root_dir, pkg_version, pkg_work_dir) outfile = pkg_work_dir + CLIENT_DATA_COMPONENT_PACKAGE_FILE cmd = ['/usr/bin/pkgbuild'] cmd << '--root' cmd << root_dir.to_s cmd << '--identifier' cmd << client_data_package_identifier cmd << '--version' cmd << pkg_version cmd << '--install-location' cmd << '/' cmd << '--sign' cmd << Xolo::Server.config.pkg_signing_identity cmd << '--keychain' cmd << Xolo::Server::Configuration::PKG_SIGNING_KEYCHAIN.to_s cmd << outfile.to_s log_debug "Command to build component pkg '#{CLIENT_DATA_COMPONENT_PACKAGE_FILE}': #{cmd.join(' ')}" stdouterr, exit_status = Open3.capture2e(*cmd) raise "Error creating #{client_data_package_file}: #{stdouterr}" unless exit_status.success? outfile end |
#build_dist_client_data_pkg_file(component_pkg_file, pkg_version, pkg_work_dir) ⇒ Pathname
Build the distribution package for the xolo-client-data JSON file NOTE: no need to shellescape the paths, since we are using the array version of Open3.capture2e
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/xolo/server/helpers/client_data.rb', line 362 def build_dist_client_data_pkg_file(component_pkg_file, pkg_version, pkg_work_dir) pkg_file = pkg_work_dir + client_data_package_file cmd = ['/usr/bin/productbuild'] cmd << '--package' cmd << component_pkg_file.to_s cmd << '--identifier' cmd << client_data_package_identifier cmd << '--version' cmd << pkg_version cmd << '--sign' cmd << Xolo::Server.config.pkg_signing_identity cmd << '--keychain' cmd << Xolo::Server::Configuration::PKG_SIGNING_KEYCHAIN.to_s cmd << pkg_file.to_s log_debug "Command to build distribution pkg '#{client_data_package_file}': #{cmd.join(' ')}" stdouterr, exit_status = Open3.capture2e(*cmd) raise "Error creating #{client_data_package_file}: #{stdouterr}" unless exit_status.success? pkg_file end |
#client_app_source ⇒ Pathname
Returns the path to the client executable 'xolo' in the ruby gem.
416 417 418 419 420 421 422 423 |
# File 'lib/xolo/server/helpers/client_data.rb', line 416 def client_app_source # parent 1 == helpers # parent 2 == server # parent 3 == xolo # parent 4 == lib # parent 5 == root @client_app ||= Pathname.new(__FILE__)..parent.parent.parent.parent.parent + 'data' + 'client' + 'xolo' end |
#client_data_auto_policy_name ⇒ String
The name of the Jamf::Policy object that installs the xolo-client-data package automatically on all managed Macs NOTE: Set the category to Xolo::Server::JAMF_XOLO_CATEGORY
124 125 126 |
# File 'lib/xolo/server/helpers/client_data.rb', line 124 def client_data_auto_policy_name @client_data_auto_policy_name ||= "#{client_data_package_name}-auto" end |
#client_data_filename ⇒ String
The name of the client-data JSON file in the xolo-client-data package this is the file that is installed onto managed Macs in /Library/Application Support/xolo/
93 94 95 |
# File 'lib/xolo/server/helpers/client_data.rb', line 93 def client_data_filename @client_data_filename ||= Xolo::Server.config.test_server ? "test-#{CLIENT_DATA_STR}.json" : "#{CLIENT_DATA_STR}.json" end |
#client_data_hash ⇒ Hash
Returns the data to put in the xolo-client-data JSON file.
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 |
# File 'lib/xolo/server/helpers/client_data.rb', line 388 def client_data_hash cdh = { titles: {} } return cdh if all_titles.empty? all_title_objects(refresh: true).each do |title| cdh[:titles][title.title] = title.to_h cdh[:titles][title.title][:versions] = title.version_objects.map(&:to_h) # the client uses the version_script to determine if a title is installed cdh[:titles][title.title][:version_script] = title.version_script_contents if title.version_script # add the forced_exclusion_group_name if any cdh[:titles][title.title][:excluded_groups] << Xolo::Server.config.forced_exclusion if Xolo::Server.config.forced_exclusion # add the frozen group name to the excluded_groups array cdh[:titles][title.title][:excluded_groups] << title.jamf_frozen_group_name if title.jamf_frozen_group_name end # TESTING # outfile = Pathname.new('/tmp/client-data.json') # outfile.pix_save JSON.pretty_generate(cdh) cdh end |
#client_data_jpackage ⇒ Jamf::JPackage
Returns the xolo-client-data package object.
148 149 150 151 152 153 154 |
# File 'lib/xolo/server/helpers/client_data.rb', line 148 def client_data_jpackage return @client_data_jpackage if @client_data_jpackage @client_data_jpackage = Jamf::JPackage.fetch packageName: client_data_package_name, cnx: jamf_cnx rescue Jamf::NoSuchItemError @client_data_jpackage = create_client_data_jamf_package end |
#client_data_manual_policy_name ⇒ String
The name of the Jamf::Policy object that installs the xolo-client-data package manually on a managed Mac
132 133 134 |
# File 'lib/xolo/server/helpers/client_data.rb', line 132 def client_data_manual_policy_name @client_data_manual_policy_name ||= "#{client_data_package_name}-manual" end |
#client_data_manual_policy_trigger ⇒ String
The trgger event for the manual policy to update the client data JSON file
139 140 141 |
# File 'lib/xolo/server/helpers/client_data.rb', line 139 def client_data_manual_policy_trigger @client_data_manual_policy_trigger ||= Xolo::Server.config.test_server ? 'test-update-xolo-client-data' : 'update-xolo-client-data' end |
#client_data_package_file ⇒ String
The name of the package file that installs the xolo-client-data JSON file
115 116 117 |
# File 'lib/xolo/server/helpers/client_data.rb', line 115 def client_data_package_file @client_data_package_file ||= "#{client_data_package_name}.pkg" end |
#client_data_package_identifier ⇒ String
The package identifier for the xolo-client-data package
100 101 102 |
# File 'lib/xolo/server/helpers/client_data.rb', line 100 def client_data_package_identifier @client_data_package_identifier ||= Xolo::Server.config.test_server ? "com.pixar.xolotest.#{CLIENT_DATA_STR}" : "com.pixar.xolo.#{CLIENT_DATA_STR}" end |
#client_data_package_name ⇒ String
The name of the Jamf Package object that contains the xolo client-data NOTE: Set the category to Xolo::Server::JAMF_XOLO_CATEGORY
108 109 110 |
# File 'lib/xolo/server/helpers/client_data.rb', line 108 def client_data_package_name @client_data_package_name ||= "#{jamf_obj_name_pfx_base}#{CLIENT_DATA_STR}" end |
#client_data_testing ⇒ Object
temp
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/xolo/server/helpers/client_data.rb', line 427 def client_data_testing this_file = Pathname.new(__FILE__). log_debug "this_file: #{this_file}" # parent 1 == helpers # parent 2 == server # parent 3 == xolo # parent 4 == lib # parent 5 == root data_dir = this_file.parent.parent.parent.parent.parent + 'data' log_debug "data_dir: #{data_dir}" log_debug "data_dir exists? #{data_dir.exist?}" log_debug "data_dir children: #{data_dir.children}" client_dir = data_dir + 'client' log_debug "client_dir: #{client_dir}" log_debug "client_dir exists? #{client_dir.exist?}" log_debug "client_dir children: #{client_dir.children}" client_app = client_dir + 'xolo' log_debug "client_app: #{client_app}" log_debug "client_app exists? #{client_app.exist?}" end |
#create_client_data_jamf_package ⇒ Jamf::JPackage
Create and return the xolo-client-data package in Jamf Pro
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/xolo/server/helpers/client_data.rb', line 205 def create_client_data_jamf_package progress "Jamf: Creating package object '#{client_data_package_name}'" info = "Installs the xolo client data JSON file into /Library/Application Support/xolo/#{client_data_filename}" # Create the package pkg = Jamf::JPackage.create( cnx: jamf_cnx, packageName: client_data_package_name, fileName: client_data_package_file, categoryId: jamf_xolo_category_id, info: info ) pkg.save # .pkg files are not uploaded here, but in the upload_client_data_package method log_debug "Jamf: Created package '#{client_data_package_name}'" pkg rescue => e raise "Jamf: Error creating Jamf::JPackage '#{client_data_package_name}': #{e.class}: #{e}" end |
#create_client_data_policies_if_needed ⇒ void
This method returns an undefined value.
Create the xolo-client-data policies in Jamf Pro
233 234 235 236 237 238 239 240 241 |
# File 'lib/xolo/server/helpers/client_data.rb', line 233 def create_client_data_policies_if_needed all_pol_names = Jamf::Policy.all_names(cnx: jamf_cnx) create_client_data_policy client_data_auto_policy_name unless all_pol_names.include? client_data_auto_policy_name return if all_pol_names.include? client_data_manual_policy_name create_client_data_policy client_data_manual_policy_name end |
#create_client_data_policy(pol_name) ⇒ void
This method returns an undefined value.
Create a xolo-client-data install policy in Jamf Pro
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/xolo/server/helpers/client_data.rb', line 249 def create_client_data_policy(pol_name) progress "Jamf: Creating policy '#{pol_name}'" # Create the policy and set common attributes pol = Jamf::Policy.create name: pol_name, cnx: jamf_cnx pol.category = Xolo::Server::JAMF_XOLO_CATEGORY pol.add_package client_data_package_name # scope to all computers pol.scope.set_all_targets # exclude the forced exclusion group if any if valid_forced_exclusion_group_name pol.scope.set_exclusions :computer_groups, [valid_forced_exclusion_group_name] log_info "Jamf: Excluded computer group: #{Xolo::Server.config.forced_exclusion} from policy '#{pol_name}'" end # Set the trigger event and frequency if pol_name == client_data_auto_policy_name pol.set_trigger_event :checkin, true pol.set_trigger_event :custom, Xolo::BLANK pol.frequency = :daily elsif pol_name == client_data_manual_policy_name pol.set_trigger_event :checkin, false pol.set_trigger_event :custom, client_data_manual_policy_name pol.frequency = :ongoing else err_msg = "Jamf: Invalid policy name '#{pol_name}' must be #{client_data_auto_policy_name} or #{client_data_manual_policy_name}" log_err err_msg, alert: true return end pol.enable pol.save log_info "Jamf: Created policy '#{pol_name}'" end |
#create_new_client_data_pkg_file ⇒ Pathname
Create the xolo-client-data package installer file. The xolo client executable is deployed as a separate thing in a Xolo Title
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 |
# File 'lib/xolo/server/helpers/client_data.rb', line 301 def create_new_client_data_pkg_file pkg_version = Time.now.strftime '%Y%m%d.%H%M%S.%6N' work_dir_prefix = "#{client_data_package_name}-#{pkg_version}" pkg_work_dir = Pathname.new(Dir.mktmpdir(work_dir_prefix)) # The client data JSON file root_dir = pkg_work_dir + 'pkgroot' xolo_client_dir = root_dir + 'Library' + 'Application Support' + 'xolo' xolo_client_dir.mkpath client_data_file = xolo_client_dir + client_data_filename client_data_file.pix_save JSON.pretty_generate(client_data_hash) # build the component package progress "Jamf: Creating new client-data pkg file '#{client_data_package_file}'", log: :info unlock_signing_keychain component_pkg_file = build_component_client_data_pkg_file(root_dir, pkg_version, pkg_work_dir) build_dist_client_data_pkg_file(component_pkg_file, pkg_version, pkg_work_dir) end |
#flush_client_data_policy_logs ⇒ void
This method returns an undefined value.
Flush the logs for the xolo-client-data policies
290 291 292 293 294 |
# File 'lib/xolo/server/helpers/client_data.rb', line 290 def flush_client_data_policy_logs progress "Jamf: Flushing logs for policy #{client_data_auto_policy_name}", log: :info pol = Jamf::Policy.fetch name: client_data_auto_policy_name, cnx: jamf_cnx pol.flush_logs end |
#update_client_data(post_maint: false) ⇒ void
This method returns an undefined value.
update the xolo-client-data package and the policy that installs it
This package installs a JSON file with data about all titles and versions for use by the xolo client on managed Macs.
This process is protected by a mutex to prevent multiple updates at the same time.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/xolo/server/helpers/client_data.rb', line 167 def update_client_data(post_maint: false) # don't do anything if we are in developer/test mode if Xolo::Server.config.developer_mode? log_debug 'Jamf: Skipping client-data update in developer mode' return end # don't do anything if maintenance is running, # the main process will call this method once at the end if Xolo::Server::Helpers::Maintenance.maint_running? && !post_maint log_debug 'Jamf: Skipping client-data update while maintenance is running' return end log_info 'Jamf: Updating client-data package' # TODO: Use Concurrent Ruby instead of Mutex mutex = Xolo::Server::Helpers::ClientData.client_data_mutex until mutex.try_lock progress 'Waiting for another client data update to finish', log: :info sleep 5 end new_pkg = create_new_client_data_pkg_file upload_to_dist_point client_data_jpackage, new_pkg create_client_data_policies_if_needed flush_client_data_policy_logs ensure mutex.unlock if mutex&.owned? end |