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 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 ⇒ 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
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/xolo/server/helpers/client_data.rb', line 321 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
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 |
# File 'lib/xolo/server/helpers/client_data.rb', line 353 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.
407 408 409 410 411 412 413 414 |
# File 'lib/xolo/server/helpers/client_data.rb', line 407 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.
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/xolo/server/helpers/client_data.rb', line 379 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
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
# File 'lib/xolo/server/helpers/client_data.rb', line 418 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
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
# File 'lib/xolo/server/helpers/client_data.rb', line 196 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
224 225 226 227 228 229 230 231 232 |
# File 'lib/xolo/server/helpers/client_data.rb', line 224 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
240 241 242 243 244 245 246 247 248 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 |
# File 'lib/xolo/server/helpers/client_data.rb', line 240 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
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/xolo/server/helpers/client_data.rb', line 292 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
281 282 283 284 285 |
# File 'lib/xolo/server/helpers/client_data.rb', line 281 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 ⇒ 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.
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
# File 'lib/xolo/server/helpers/client_data.rb', line 165 def update_client_data # 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 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 |