ruby-jss 4.2.0b2 → 4.2.0
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.
- checksums.yaml +4 -4
- data/CHANGES.md +25 -7
- data/README-2.0.0.md +19 -8
- data/README.md +43 -30
- data/lib/jamf/api/classic/api_objects/patch_title.rb +0 -2
- data/lib/jamf/api/jamf_pro/api_objects/api_client.rb +3 -0
- data/lib/jamf/api/jamf_pro/api_objects/api_role.rb +3 -0
- data/lib/jamf/api/jamf_pro/api_objects/j_package.rb +307 -119
- data/lib/jamf/api/jamf_pro/api_objects/managed_software_updates/plan.rb +237 -0
- data/lib/jamf/api/jamf_pro/api_objects/managed_software_updates.rb +291 -0
- data/lib/jamf/api/jamf_pro/base_classes/oapi_object.rb +8 -0
- data/lib/jamf/api/jamf_pro/mixins/collection_resource.rb +23 -3
- data/lib/jamf/api/jamf_pro/mixins/filterable.rb +8 -0
- data/lib/jamf/api/jamf_pro/mixins/jpapi_resource.rb +17 -3
- data/lib/jamf/api/jamf_pro/mixins/macos_managed_updates.rb +2 -0
- data/lib/jamf/api/jamf_pro/mixins/prestage.rb +3 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/account_group.rb +123 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/account_preferences_v1.rb +105 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/assign_remove_profile_response_sync_state.rb +112 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/auth_account_v1.rb +159 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/authentication_type.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/{device_enrollment_disown_body.rb → available_updates.rb} +14 -10
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_application.rb +124 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_attachment.rb +102 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_certificate.rb +151 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_configuration_profile.rb +118 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching.rb +372 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_alert.rb +120 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_cache_detail.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_data_migration_error.rb +98 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_data_migration_error_user_info.rb +89 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_parent.rb +131 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_parent_alert.rb +105 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_parent_capabilities.rb +124 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_parent_details.rb +118 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_content_caching_parent_local_network.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_disk.rb +143 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_disk_encryption.rb +120 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_extension_attribute.rb +175 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_font.rb +93 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_general.rb +244 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_hardware.rb +264 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_ibeacon.rb +81 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_inventory.rb +276 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_inventory_file_vault.rb +127 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_licensed_software.rb +88 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_local_user_account.rb +196 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_mdm_capability.rb +88 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_operating_system.rb +148 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_package_receipts.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_partition.rb +145 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_partition_encryption.rb +94 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_partition_file_vault2_state.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_plugin.rb +93 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_prestage_v3.rb +129 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_printer.rb +99 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_purchase.rb +158 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_remote_management.rb +88 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_section.rb +109 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_security.rb +193 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_service.rb +81 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_software_update.rb +93 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_storage.rb +90 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/computer_user_and_location.rb +131 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/dss_declaration.rb +111 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/dss_declarations.rb +88 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/enrollment_method.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/group_membership.rb +94 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/inventory_preload_extension_attribute.rb +89 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/location_information.rb +146 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/{package_manifest.rb → location_information_v2.rb} +35 -37
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan.rb +181 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_event_store.rb +85 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_group_post.rb +99 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_post.rb +98 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_post_response.rb +100 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_toggle.rb +115 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_toggle_status.rb +169 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plan_toggle_status_wrapper.rb +91 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_plans.rb +102 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_status.rb +173 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/managed_software_update_statuses.rb +102 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/mobile_device_prestage_name_v2.rb +94 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/mobile_device_prestage_names_v2.rb +118 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_configuration_post.rb +142 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_device.rb +105 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_device_post.rb +97 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_device_response.rb +96 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_group_post.rb +96 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_search_results.rb +89 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/plan_status.rb +127 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/{device_enrollment_prestage.rb → prestage_purchasing_information.rb} +51 -88
- data/lib/jamf/api/jamf_pro/oapi_schemas/prestage_purchasing_information_v2.rb +174 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/prestage_scope_assignment_v2.rb +94 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas/v1_site.rb +91 -0
- data/lib/jamf/api/jamf_pro/oapi_schemas.rb +24 -1
- data/lib/jamf/composer.rb +114 -107
- data/lib/jamf/oapi_validate.rb +1 -0
- data/lib/jamf/version.rb +1 -1
- data/test/bin/runtests +2 -2
- data/test/lib/jamf_test/collection_tests.rb +10 -2
- data/test/tests/computer_group.rb +29 -12
- data/test/tests/{jp_building.rb → j_building.rb} +2 -2
- data/test/tests/j_package.rb +47 -0
- metadata +87 -8
@@ -25,6 +25,8 @@
|
|
25
25
|
|
26
26
|
# frozen_string_literal: true
|
27
27
|
|
28
|
+
require 'cfpropertylist'
|
29
|
+
|
28
30
|
# The main Module
|
29
31
|
module Jamf
|
30
32
|
|
@@ -102,6 +104,13 @@ module Jamf
|
|
102
104
|
# which usually only identifies ':id'
|
103
105
|
ALT_IDENTIFIERS = %i[packageName fileName].freeze
|
104
106
|
|
107
|
+
# If the object does not have a 'name' attribute, this is the attribute
|
108
|
+
# that holds its name. Used to allow referencing objects by 'name',
|
109
|
+
# creates a alias of the attribute called "name" and "name=",
|
110
|
+
# and allows the use of "name:" as an identifier in the .fetch, .valid_id and
|
111
|
+
# similar methods.
|
112
|
+
OBJECT_NAME_ATTR = :packageName
|
113
|
+
|
105
114
|
# Must define this when extending Filterable
|
106
115
|
FILTER_KEYS = %i[
|
107
116
|
id fileName packageName categoryId info notes manifestFileName cloudTransferStatus
|
@@ -126,7 +135,7 @@ module Jamf
|
|
126
135
|
# The hashType value in the API or manifests for md5
|
127
136
|
CHECKSUM_HASH_TYPE_MD5 = 'MD5'
|
128
137
|
|
129
|
-
# The hashType value in the API for sha256 - IF it exists
|
138
|
+
# The hashType value in the API for sha256 - IF it exists?
|
130
139
|
CHECKSUM_HASH_TYPE_SHA256 = 'SHA_256'
|
131
140
|
|
132
141
|
# The hashType value in MDM deploy manifests for sha256
|
@@ -149,7 +158,15 @@ module Jamf
|
|
149
158
|
# fileName, with spaces converted to dashes.
|
150
159
|
MANIFEST_FILENAME_DEFAULT_SUFFIX = '-manifest.plist'
|
151
160
|
|
152
|
-
#
|
161
|
+
# If no manifest bundle identifier is provided, this will be used before
|
162
|
+
# the packageName.
|
163
|
+
MANIFEST_BUNDLE_ID_PREFIX = 'com.pixar.ruby-jss.'
|
164
|
+
|
165
|
+
# if no manifest bundle version is provided, this will be used.
|
166
|
+
MANIFEST_BUNDLE_VERSION_DEFAULT = '0'
|
167
|
+
|
168
|
+
# Not doing chunking by default in generated manifests,
|
169
|
+
# but if we do, we'll use this
|
153
170
|
MANIFEST_CHUNK_SIZE = 1024 * 1024 * 10 # 10MB
|
154
171
|
|
155
172
|
MANIFEST_PLIST_TEMPLATE = {
|
@@ -164,10 +181,11 @@ module Jamf
|
|
164
181
|
], # end assets array,
|
165
182
|
metadata: {
|
166
183
|
'kind' => 'software',
|
167
|
-
'bundle-identifier' =>
|
168
|
-
'bundle-version' =>
|
184
|
+
'bundle-identifier' => "#{MANIFEST_BUNDLE_ID_PREFIX}example",
|
185
|
+
'bundle-version' => MANIFEST_BUNDLE_VERSION_DEFAULT,
|
169
186
|
'title' => 'title',
|
170
|
-
'sizeInBytes' => 1
|
187
|
+
'sizeInBytes' => 1,
|
188
|
+
'sha256-whole' => 'sha256-goes-here'
|
171
189
|
} # end metadata
|
172
190
|
} # end hash
|
173
191
|
] # end items array
|
@@ -210,8 +228,8 @@ module Jamf
|
|
210
228
|
# for all Jamf::JPackage objects in this ruby session.
|
211
229
|
#
|
212
230
|
# Setting this in the config or here means you don't have to provide a base URL
|
213
|
-
# each time when calling #generate_manifest, however you can still provide
|
214
|
-
# at that time to override any default.
|
231
|
+
# each time when calling #generate_manifest or #upload, however you can still provide
|
232
|
+
# one at that time to override any default.
|
215
233
|
#
|
216
234
|
# Normally, the package's fileName is appended to this URL to generate the full
|
217
235
|
# download URL. E.g. for a package with a fileName 'my-app.pkg', with the base URL of
|
@@ -244,18 +262,22 @@ module Jamf
|
|
244
262
|
#####################################
|
245
263
|
|
246
264
|
# Checksums
|
265
|
+
#
|
247
266
|
# Packages in Jamf Pro can have a checksum in either MD5 or SHA512, or possibly
|
248
267
|
# SHA256 - none of our 1500 pkgs have 256, but given the existence of the sha256
|
249
268
|
# attribute in the API data, I'm assuming it existed at some point, and behaves like
|
250
269
|
# md5 (read on)
|
251
270
|
#
|
252
|
-
# In all cases, the hashType indicates the type of checksum, as a string,
|
253
|
-
# 'MD5', 'SHA_256', or 'SHA_512'.
|
271
|
+
# In all cases, the hashType attribute indicates the type of checksum, as a string,
|
272
|
+
# one of 'MD5', 'SHA_256', or 'SHA_512'.
|
254
273
|
#
|
255
|
-
# In the case of md5 and sha256, the
|
274
|
+
# In the case of md5 and sha256, the actual digest value (the checksum) is in the
|
256
275
|
# 'md5' or 'sha256' attribute. In the case of sha512, the digest is in the 'hashValue'
|
257
276
|
# attribute.
|
258
|
-
# In anycase, the digest value will be stored in the checksum attribute
|
277
|
+
# In anycase, the digest value will also be stored in the checksum attribute
|
278
|
+
#
|
279
|
+
# NOTE: This is the checksum used when installing via a Policy.
|
280
|
+
# The checksum(s) used when deploying via MDM is stored in the manifest.
|
259
281
|
#
|
260
282
|
# @return [String] the checksum of the package, either an MD5, SHA256, or SHA512
|
261
283
|
# digest of the package file.
|
@@ -333,14 +355,42 @@ module Jamf
|
|
333
355
|
#####################################
|
334
356
|
|
335
357
|
# @return [Pathname] the local receipt when this pkg is installed by a policy
|
358
|
+
#############################
|
336
359
|
def receipt
|
337
360
|
# the receipt is the filename with any .zip extension removed.
|
338
361
|
fileName ? (Jamf::Client::RECEIPTS_FOLDER + fileName.to_s.sub(/.zip$/, '')) : nil
|
339
362
|
end
|
340
363
|
|
364
|
+
# Change the os_requirements field in the JSS
|
365
|
+
# E.g. 10.5, 10.5.3, 10.6.x
|
366
|
+
#
|
367
|
+
# Extra feature: Minumum OS's can now be specified as a
|
368
|
+
# string using the notation ">=10.6.7".
|
369
|
+
#
|
370
|
+
# @see Jamf.expand_min_os
|
371
|
+
#
|
372
|
+
# @param new_val [String,Array<String>] comma-separated string, or array of os versions
|
373
|
+
#
|
374
|
+
# @return [void]
|
375
|
+
#############################
|
376
|
+
def osRequirements=(new_val)
|
377
|
+
# make sure we have an array
|
378
|
+
new_val = [new_val].flatten.compact.uniq.map(&:to_s)
|
379
|
+
new_val.map! do |vers|
|
380
|
+
vers.start_with?('>=') ? Jamf.expand_min_os(vers) : vers
|
381
|
+
end
|
382
|
+
|
383
|
+
orig_osRequirements = osRequirements
|
384
|
+
@osRequirements = new_val.join(', ')
|
385
|
+
note_unsaved_change :osRequirements, orig_osRequirements
|
386
|
+
end
|
387
|
+
|
341
388
|
# Recalculate the checksum of the package file from a given filepath, and update the
|
342
389
|
# object's checksum and hashValue attributes.
|
343
390
|
#
|
391
|
+
# NOTE: This updates the checksum used when installing via a Policy.
|
392
|
+
# The checksum(s) used when deploying via MDM is stored in the manifest.
|
393
|
+
#
|
344
394
|
# You will need to call #save on the object to save the new checksum to the server.
|
345
395
|
#
|
346
396
|
# New checksums are always SHA512
|
@@ -368,38 +418,65 @@ module Jamf
|
|
368
418
|
self.class.calculate_checksum(filepath, hashType) == checksum
|
369
419
|
end
|
370
420
|
|
371
|
-
# @return [String]
|
421
|
+
# @return [String] A default if none is set explicitly
|
372
422
|
##############################
|
373
423
|
def default_manifestFileName
|
374
424
|
"#{fileName.gsub(' ', '-')}#{MANIFEST_FILENAME_DEFAULT_SUFFIX}"
|
375
425
|
end
|
376
426
|
|
377
|
-
# Set the manifest from a local file or XML plist
|
378
|
-
# If from a file,
|
427
|
+
# Set the manifest from a local file or a String containing an XML plist.
|
428
|
+
# If from a file, the manifestFileName attribute is set to the filename
|
429
|
+
#
|
430
|
+
# To automatically generate a manifest plist for this package from a
|
431
|
+
# locally-readable .pkg file, use #generate_manifest
|
432
|
+
#
|
433
|
+
# All manifests require a valid URL for downloading the .pkg file when
|
434
|
+
# installing on a client.
|
435
|
+
#
|
436
|
+
# No validation of the manifest is done here.
|
379
437
|
#
|
380
|
-
#
|
438
|
+
# DEPLOYING VIA MDM:
|
381
439
|
#
|
382
|
-
# When using this method, if you want to be able to deploy the
|
383
|
-
#
|
384
|
-
# with at least the following keys
|
385
|
-
#
|
386
|
-
#
|
387
|
-
#
|
388
|
-
#
|
389
|
-
#
|
390
|
-
#
|
440
|
+
# When using this method, if you want to be able to deploy the package using
|
441
|
+
# deploy_via_mdm, the manifest MUST include a metadata dictionary
|
442
|
+
# with at least the following keys:
|
443
|
+
# - 'kind' = 'software'
|
444
|
+
# - 'bundle-identifier' that preferably matches the bundle identifier of the pkg
|
445
|
+
# - 'bundle-version' = that preferably matches the version of the pkg
|
446
|
+
# - 'title' = the name of the pkg or what it installs
|
447
|
+
# - 'sizeInBytes' = the size of the .pkg in bytes
|
448
|
+
# as well as one of these non-standard keys:
|
449
|
+
# - 'sha256-whole' = the SHA256 digest of the whole file, regardless of chunking data in the 'assets' array
|
450
|
+
# OR
|
451
|
+
# - 'md5-whole' = the MD5 digest of the whole file, regardless of chunking data in the 'assets' array
|
391
452
|
#
|
392
|
-
#
|
453
|
+
# The non-standard keys are because the Jamf Pro API endpoint for deploying via MDM requires
|
454
|
+
# a whole-file checksum even if the file is chunked in the manifest.
|
455
|
+
#
|
456
|
+
# See the MANIFEST_PLIST_TEMPLATE constant for an example of the data structure (as a ruby hash, not a plist)
|
457
|
+
#
|
458
|
+
# @param new_manifest [String, Pathname] the manifest plist data or path to a local file
|
393
459
|
#
|
394
460
|
# @return [void]
|
395
461
|
##############################
|
396
|
-
def manifest=(
|
397
|
-
if
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
462
|
+
def manifest=(new_manifest)
|
463
|
+
# if its a string but not an xml plist, assume its a path
|
464
|
+
new_manifest = Pathname.new(new_manifest) if new_manifest.is_a?(String) && !new_manifest.start_with?('<?xml')
|
465
|
+
orig_manifest = manifest
|
466
|
+
|
467
|
+
new_xml =
|
468
|
+
if new_manifest.is_a? Pathname
|
469
|
+
new_manifest.read
|
470
|
+
|
471
|
+
elsif new_manifest.is_a? String
|
472
|
+
new_manifest
|
473
|
+
|
474
|
+
else
|
475
|
+
raise ArgumentError, 'Argument must be a Pathname, or a String containing a path or an XML plist'
|
476
|
+
end
|
477
|
+
|
478
|
+
@manifest = new_xml
|
479
|
+
note_unsaved_change :manifest, orig_manifest
|
403
480
|
end
|
404
481
|
|
405
482
|
# return the manifest as a ruby hash converted from the plist
|
@@ -413,24 +490,34 @@ module Jamf
|
|
413
490
|
CFPropertyList.native_types(CFPropertyList::List.new(data: manifest).value)
|
414
491
|
end
|
415
492
|
|
416
|
-
# Generate a manifest plist (xml) for this package
|
417
|
-
# and
|
493
|
+
# Generate a manifest plist (xml) for this package from a local .pkg file,
|
494
|
+
# and update the #manifest and #manifestFileName attributes
|
418
495
|
#
|
419
496
|
# Afterwards, you will need to call #save on the object to save the new values to
|
420
497
|
# the server.
|
421
498
|
#
|
499
|
+
# See also #manifest= for setting the manifest from a file or string.
|
500
|
+
#
|
422
501
|
# The download URL used in the manifest will be the default for the class
|
423
|
-
# (if you have set one)
|
424
|
-
# class default may come from the ruby-jss config, or be set directly on the class
|
502
|
+
# (if you have set one) usually with the fileName appended. The
|
503
|
+
# class default may come from the ruby-jss config, or be set directly on the class,
|
504
|
+
# see JPackage.default_manifest_base_url=
|
425
505
|
#
|
426
506
|
# Unless set explicitly afterward using #manifestFileName= the manifest filename
|
427
507
|
# will be the fileName of the Package object, with spaces converted to dashes,
|
428
508
|
# followed by MANIFEST_FILENAME_DEFAULT_SUFFIX.
|
429
509
|
# e.g. my-app.pkg-manifest.plist
|
430
510
|
#
|
431
|
-
#
|
432
|
-
#
|
433
|
-
#
|
511
|
+
# By default, this method is invoked when uploading the pkg file using #upload
|
512
|
+
# and the opts will be passed from that method to this one.
|
513
|
+
# When invoked from #upload, the new values will be saved to the Jamf Pro server automatically.
|
514
|
+
#
|
515
|
+
# The manifests generated by this method are suitable for use in MDM deployments.
|
516
|
+
#
|
517
|
+
# If you don't provide a bundle_identifier, it will be generated from the packageName,
|
518
|
+
# prefixed with 'com.pixar.ruby-jss.' and with spaces converted to dashes.
|
519
|
+
#
|
520
|
+
# If you don't provide a bundle_version, it will be '0'
|
434
521
|
#
|
435
522
|
# @param filepath [String, Pathname] the path to a local copy of the package file for which
|
436
523
|
# this manifest is being generated. This MUST match the one uploaded to the server, as it is
|
@@ -450,10 +537,12 @@ module Jamf
|
|
450
537
|
# A common chunk size is 10MB, or 1024 * 1024 * 10.
|
451
538
|
# NOTE: Not all distribution points support chunked downloads.
|
452
539
|
#
|
453
|
-
# @option opts bundle_identifier [String, Symbol]
|
454
|
-
# Should match that in the .pkg itself
|
540
|
+
# @option opts bundle_identifier [String, Symbol] The bundle identifier of the package,
|
541
|
+
# Should match that in the .pkg itself.
|
542
|
+
# Defaults to 'com.pixar.ruby-jss.packageName' where packageName is the
|
543
|
+
# packageName with whitespace converted to dashes.
|
455
544
|
#
|
456
|
-
# @option opts bundle_version [String
|
545
|
+
# @option opts bundle_version [String] the version of the package.
|
457
546
|
# Defaults to '0'
|
458
547
|
#
|
459
548
|
# @option opts subtitle [String] a subtitle for the package, optional
|
@@ -470,10 +559,12 @@ module Jamf
|
|
470
559
|
validate_local_file(file)
|
471
560
|
|
472
561
|
filesize = file.size
|
473
|
-
url = parse_manifest_url opts[:url], append_filename: opts[:append_filename_to_url]
|
474
562
|
|
475
563
|
# make the manifest
|
476
564
|
new_manifest = MANIFEST_PLIST_TEMPLATE.dup
|
565
|
+
|
566
|
+
url = build_manifest_url opts[:url], append_filename: opts[:append_filename_to_url]
|
567
|
+
|
477
568
|
new_manifest[:items][0][:assets][0]['url'] = url.to_s
|
478
569
|
|
479
570
|
# get the checksum(s)
|
@@ -486,25 +577,14 @@ module Jamf
|
|
486
577
|
new_manifest[:items][0][:metadata]['title'] = packageName
|
487
578
|
new_manifest[:items][0][:metadata]['subtitle'] = opts[:subtitle] if opts[:subtitle]
|
488
579
|
new_manifest[:items][0][:metadata]['sizeInBytes'] = filesize
|
489
|
-
new_manifest[:items][0][:metadata]['bundle-identifier'] =
|
490
|
-
|
491
|
-
|
492
|
-
# TESTING - store the whole-file checksum in
|
493
|
-
# manifest[:items][0][:metadata]['sha256-whole']. taking it from
|
494
|
-
# manifest[:items][0][:assets][0]['sha256s'][0], if available, or generate it if needed
|
495
|
-
# It is used by the deploy_via_mdm method.
|
496
|
-
# The test will be to deploy this pkg via MDM in a prestage, and see
|
497
|
-
# if the 'unknown' hash key 'sha256-whole' causes a problem installing.
|
498
|
-
new_manifest[:items][0][:metadata]['sha256-whole'] =
|
499
|
-
if new_manifest[:items][0][:assets][0]['sha256s'].size == 1
|
500
|
-
new_manifest[:items][0][:assets][0]['sha256s'][0]
|
501
|
-
else
|
502
|
-
new_manifest[:items][0][:assets][0]['sha256s'] = [Digest::SHA256.hexdigest(file.read)]
|
503
|
-
end
|
580
|
+
new_manifest[:items][0][:metadata]['bundle-identifier'] =
|
581
|
+
(opts[:bundle_identifier] || "#{MANIFEST_BUNDLE_ID_PREFIX}#{packageName.gsub(/\s+/, '-')}")
|
582
|
+
new_manifest[:items][0][:metadata]['bundle-version'] = opts[:bundle_version] if opts[:bundle_version]
|
504
583
|
|
505
584
|
plist = CFPropertyList::List.new
|
506
585
|
plist.value = CFPropertyList.guess(new_manifest)
|
507
|
-
self.manifest = plist.to_str
|
586
|
+
self.manifest = plist.to_str(CFPropertyList::List::FORMAT_XML, formatted: true)
|
587
|
+
|
508
588
|
self.manifestFileName = default_manifestFileName
|
509
589
|
end
|
510
590
|
|
@@ -524,12 +604,16 @@ module Jamf
|
|
524
604
|
# the url is not valid.
|
525
605
|
#
|
526
606
|
# @param given_url [String] the URL to use, if provided
|
607
|
+
# @param append_filename [Boolean] should the filename be appended to the URL?
|
527
608
|
#
|
528
609
|
# @return [URI] the URI object for the URL
|
529
610
|
##############################
|
530
|
-
def
|
611
|
+
def build_manifest_url(given_url = nil, append_filename: true)
|
531
612
|
url = given_url || self.class.default_manifest_base_url
|
532
|
-
|
613
|
+
unless url
|
614
|
+
raise ArgumentError,
|
615
|
+
'No URL for manifest. Pass one with url: or set one with Jamf::JPackage.default_manifest_base_url=, or set package_manifest_base_url in the ruby-jss.conf file.'
|
616
|
+
end
|
533
617
|
|
534
618
|
# append the filename to the URL if needed
|
535
619
|
url = "#{url.to_s.chomp('/')}/#{CGI.escape fileName}" unless append_filename == false
|
@@ -537,9 +621,10 @@ module Jamf
|
|
537
621
|
# check validity and return
|
538
622
|
URI.parse url
|
539
623
|
end
|
540
|
-
private :
|
624
|
+
private :build_manifest_url
|
541
625
|
|
542
|
-
# calculate the manifest checksum[s] for a given file, and store in the manifest data
|
626
|
+
# calculate the manifest checksum[s] for a given file, and store in the manifest data.
|
627
|
+
# We only do SHA256, but Apple supports MD5 as well.
|
543
628
|
#
|
544
629
|
# @param file [Pathname] the path to the file to checksum
|
545
630
|
# @param chunk_size [Integer] the size of each chunk in the manifest, in bytes.
|
@@ -564,6 +649,18 @@ module Jamf
|
|
564
649
|
new_manifest[:items][0][:assets][0]['sha256-size'] = file.size
|
565
650
|
new_manifest[:items][0][:assets][0]['sha256s'] = [Digest::SHA256.hexdigest(file.read)]
|
566
651
|
end
|
652
|
+
|
653
|
+
# Store the whole-file checksum in
|
654
|
+
# manifest[:items][0][:metadata]['sha256-whole']. taking it from
|
655
|
+
# manifest[:items][0][:assets][0]['sha256s'][0], if available, or generate it if needed
|
656
|
+
# It is used by the deploy_via_mdm method.
|
657
|
+
# This value is required for MDM deployments, even if the file is chunked in the manifest.
|
658
|
+
new_manifest[:items][0][:metadata]['sha256-whole'] =
|
659
|
+
if new_manifest[:items][0][:assets][0]['sha256s'].size == 1
|
660
|
+
new_manifest[:items][0][:assets][0]['sha256s'][0]
|
661
|
+
else
|
662
|
+
Digest::SHA256.hexdigest(file.read)
|
663
|
+
end
|
567
664
|
end
|
568
665
|
private :calculate_manifest_checksums
|
569
666
|
|
@@ -583,8 +680,10 @@ module Jamf
|
|
583
680
|
end
|
584
681
|
private :append_manifest_image
|
585
682
|
|
586
|
-
# Upload a package file to Jamf Pro
|
587
|
-
#
|
683
|
+
# Upload a package file to Jamf Pro.
|
684
|
+
#
|
685
|
+
# WARNING: This will automatically call #save, saving any pending changes to
|
686
|
+
# the Jamf Pro server!
|
588
687
|
#
|
589
688
|
# This uses the Jamf Pro API to upload the file via the package/upload endpoint.
|
590
689
|
# If you don't use an appropriate primary distribution point, this may not work.
|
@@ -594,44 +693,44 @@ module Jamf
|
|
594
693
|
# If that filename is in use by some other package, you'll get an error:
|
595
694
|
# Field: fileName, Error: DUPLICATE_FIELD duplicate name
|
596
695
|
#
|
597
|
-
#
|
696
|
+
# This will automatically call #save at least once, and possibly twice.
|
598
697
|
# First, in order to ensure the correct fileName in Jamf based on the file being uploaded,
|
599
698
|
# and second, in order to update the checksum and manifest in Jamf Pro, if needed.
|
600
699
|
# *** Any other outstanding changes will also be saved!
|
601
700
|
#
|
701
|
+
# After uploading, the response from the server is in the #upload_response attribute,
|
702
|
+
# with a timestamp added to the data from the API.
|
703
|
+
#
|
602
704
|
# @param filepath [String, Pathname] the path to the package file to upload
|
603
705
|
#
|
604
706
|
# @param opts[Hash] a hash of keyword arguments
|
605
707
|
#
|
606
708
|
# @option opts :update_checksum [Boolean] update the checksum of the package in Jamf Pro.
|
607
709
|
# Defaults to true. All new checksums are SHA_512.
|
710
|
+
# WARNING: If you set this to false, the checksum in the object will not be updated
|
711
|
+
# and installs may fail. Be sure to set it to the correct value yourself.
|
608
712
|
#
|
609
713
|
# @option opts :update_manifest [Boolean] update the manifest of the package in Jamf Pro
|
610
|
-
# Defaults to true
|
714
|
+
# Defaults to true.
|
715
|
+
# WARNING: If you set this to false, the manifest in the object will not be updated
|
716
|
+
# and PreStage & MDM deployments may fail. Be sure to set it to the correct value
|
717
|
+
# using #generate_manifest or #manifest= yourself.
|
611
718
|
#
|
612
|
-
# @options opts url [String]
|
613
|
-
# defaults to the class default
|
719
|
+
# @options opts url [String] See #generate_manifest
|
614
720
|
#
|
615
|
-
# @option opts append_filename_to_url [Boolean]
|
616
|
-
# defaults to true.
|
617
|
-
# If false, the url given must be the full URL to download the individual package file.
|
721
|
+
# @option opts append_filename_to_url [Boolean] See #generate_manifest
|
618
722
|
#
|
619
|
-
# @option opts chunk_size [Integer]
|
620
|
-
# If omitted, the whole file will be checksummed at once and downloads will not be chunked.
|
621
|
-
# A common chunk size is 10MB, or 1024 * 1024 * 10.
|
622
|
-
# NOTE: Not all distribution points support chunked downloads.
|
723
|
+
# @option opts chunk_size [Integer] See #generate_manifest
|
623
724
|
#
|
624
|
-
# @option opts bundle_identifier [String
|
625
|
-
# Should match that in the .pkg itself, but defaults to 'xolo.fileName'
|
725
|
+
# @option opts bundle_identifier [String] See #generate_manifest
|
626
726
|
#
|
627
|
-
# @option opts bundle_version [String
|
628
|
-
# Defaults to '0'
|
727
|
+
# @option opts bundle_version [String] See #generate_manifest
|
629
728
|
#
|
630
|
-
# @option opts subtitle [String]
|
729
|
+
# @option opts subtitle [String] See #generate_manifest
|
631
730
|
#
|
632
|
-
# @option opts full_size_image_url [String]
|
731
|
+
# @option opts full_size_image_url [String] See #generate_manifest
|
633
732
|
#
|
634
|
-
# @option opts display_image_url [String]
|
733
|
+
# @option opts display_image_url [String] See #generate_manifest
|
635
734
|
#
|
636
735
|
# @return [void]
|
637
736
|
##############################
|
@@ -640,70 +739,72 @@ module Jamf
|
|
640
739
|
validate_local_file(file)
|
641
740
|
|
642
741
|
# update the filename if needed
|
643
|
-
# must happen before the upload
|
644
|
-
|
645
|
-
self.fileName = real_filename unless fileName == real_filename
|
646
|
-
save
|
742
|
+
# must happen before the upload so it matches the file being uploaded
|
743
|
+
self.fileName = file.basename.to_s
|
647
744
|
|
648
|
-
#
|
649
|
-
|
650
|
-
|
745
|
+
# We must save the checksum and manifest to the server before uploading
|
746
|
+
# the file, because otherwise jamf will likely overwrite the manifest
|
747
|
+
# after it uploads to the primary distribution point.
|
651
748
|
|
652
749
|
# recalulate the checksum unless told no to
|
750
|
+
# NOTE: It appears that the checksum will always be recaluclated by
|
751
|
+
# the Jamf Pro server, as MD5. If you really want our default SHA512,
|
752
|
+
# then do this again later, manually.
|
653
753
|
recalculate_checksum(file) unless opts[:update_checksum] == false
|
654
754
|
|
655
|
-
# generate a manifest
|
656
|
-
generate_manifest
|
755
|
+
# generate a manifest using the new file
|
756
|
+
generate_manifest(file, **opts) unless opts[:update_manifest] == false
|
657
757
|
|
658
|
-
# save the new checksum and manifest
|
758
|
+
# save the new fileName, checksum and manifest
|
659
759
|
save
|
760
|
+
|
761
|
+
# upload the file
|
762
|
+
@upload_response = cnx.jp_upload("#{get_path}/#{UPLOAD_ENDPOINT}", file)
|
763
|
+
@upload_response[:time] = Time.now
|
764
|
+
@upload_response
|
660
765
|
end
|
661
766
|
|
662
767
|
# Deploy this package to computers or a group via MDM.
|
663
768
|
#
|
664
769
|
# REQUIREMENTS:
|
665
|
-
# - The package must have a manifest
|
770
|
+
# - The package must have a manifest with specific data. See #manifest=
|
771
|
+
# and #generate_manifest for details.
|
666
772
|
# - The .pkg file must be a product archive (.pkg) built with Xcode or productbuild.
|
667
773
|
# (it must contain a 'Distribution' file, usually generated by those tools)
|
668
774
|
# Simple 'component' packages built with pkgbuild are not supported.
|
669
|
-
# - The .pkg file must be signed with a
|
775
|
+
# - The .pkg file must be signed with a trusted signing certificate
|
670
776
|
#
|
671
|
-
# This will send
|
672
|
-
# computers, and/or the members of a single computer group.
|
777
|
+
# This will send an MDM InstallEnterpriseApplication command to install the package
|
778
|
+
# to one or more computers, and/or the members of a single computer group.
|
673
779
|
#
|
674
780
|
# @param computer_ids [Array<Integer>,Integer] The ids of the computers to deploy to
|
675
781
|
#
|
676
782
|
# @param group_id [Integer] The id of the computer group to deploy to
|
677
783
|
#
|
678
|
-
# @param managed [Boolean] Should the installed package be managed by Jamf Pro
|
784
|
+
# @param managed [Boolean] Should the installed package be managed by Jamf Pro?
|
785
|
+
# Defaults to false. This seems to be for App Store apps only??
|
679
786
|
#
|
680
|
-
# @return [
|
787
|
+
# @return [Hash] the response from the server. see #deploy_response
|
681
788
|
##############################
|
682
789
|
def deploy_via_mdm(computer_ids: nil, group_id: nil, managed: false)
|
683
|
-
raise
|
684
|
-
raise Jamf::
|
790
|
+
raise ArgumentError, 'No computer_ids or group_id provided' unless computer_ids || group_id
|
791
|
+
raise Jamf::MissingDataError, 'No manifest set for this package' if manifest.to_s.empty?
|
792
|
+
raise Jamf::NoSuchItemError, 'This package has no id, it must be saved in Jamf Pro before uploading' unless exist?
|
685
793
|
|
686
|
-
# convert the manifest to a ruby hash
|
794
|
+
# convert the full manifest to a ruby hash
|
687
795
|
parsed_manifest = manifest_hash
|
688
796
|
|
689
797
|
# manifest data for the MDMDeploy command, which is a hash.
|
690
798
|
# hopefully some day Jamf will just use the manifest for the pkg
|
691
799
|
mdm_manifest = {}
|
692
|
-
mdm_manifest['url'] = parsed_manifest
|
693
|
-
|
694
|
-
mdm_manifest['
|
695
|
-
mdm_manifest['
|
696
|
-
mdm_manifest['
|
697
|
-
mdm_manifest['
|
698
|
-
|
699
|
-
mdm_manifest
|
700
|
-
|
701
|
-
mdm_manifest['subtitle'] = parsed_manifest['items'][0]['metadata']['subtitle'] if parsed_manifest['items'][0]['metadata']['subtitle']
|
702
|
-
|
703
|
-
parsed_manifest['items'][0]['assets'].each do |asset|
|
704
|
-
mdm_manifest['fullSizeImageURL'] = asset['url'] if asset['kind'] == 'full-size-image'
|
705
|
-
mdm_manifest['displayImageURL'] = asset['url'] if asset['kind'] == 'display-image'
|
706
|
-
end
|
800
|
+
mdm_manifest['url'] = manifest_url_for_deployment(parsed_manifest)
|
801
|
+
mdm_manifest['hash'], mdm_manifest['hashType'] = manifest_checksum_for_deployment(parsed_manifest)
|
802
|
+
mdm_manifest['bundleId'] = manifest_bundle_identifier_for_deployment(parsed_manifest)
|
803
|
+
mdm_manifest['bundleVersion'] = manifest_bundle_version_for_deployment(parsed_manifest)
|
804
|
+
mdm_manifest['title'] = manifest_title_for_deployment(parsed_manifest)
|
805
|
+
mdm_manifest['sizeInBytes'] = manifest_size_for_deployment(parsed_manifest)
|
806
|
+
|
807
|
+
set_optional_mdm_manifest_values(parsed_manifest, mdm_manifest)
|
707
808
|
|
708
809
|
# make sure the computers are in an array
|
709
810
|
computer_ids = [computer_ids].flatten.compact.uniq
|
@@ -712,13 +813,100 @@ module Jamf
|
|
712
813
|
payload = {
|
713
814
|
manifest: mdm_manifest,
|
714
815
|
installAsManaged: managed,
|
715
|
-
devices: computer_ids
|
716
|
-
groupId: group_id.to_s
|
816
|
+
devices: computer_ids
|
717
817
|
}
|
818
|
+
payload[:groupId] = group_id.to_s if group_id
|
819
|
+
|
718
820
|
# send the command
|
719
821
|
@deploy_response = cnx.post(DEPLOYMENT_ENDPOINT, payload)
|
720
822
|
end
|
721
823
|
|
824
|
+
# the URL is required for MDM deployments
|
825
|
+
# @param parsed_manifest [Hash] the parsed manifest data as a ruby hash
|
826
|
+
# @return [String] the URL in the manifest for MDM deployments
|
827
|
+
#####################################
|
828
|
+
def manifest_url_for_deployment(parsed_manifest)
|
829
|
+
url = parsed_manifest.dig 'items', 0, 'assets', 0, 'url'
|
830
|
+
raise Jamf::MissingDataError, 'No URL in the manifest' unless url
|
831
|
+
|
832
|
+
url
|
833
|
+
end
|
834
|
+
private :manifest_url_for_deployment
|
835
|
+
|
836
|
+
# whole-file checksums are required for MDM deployments
|
837
|
+
# @return [Array<String>] the checksum and checksum type in the manifest for MDM deployments
|
838
|
+
#####################################
|
839
|
+
def manifest_checksum_for_deployment(parsed_manifest)
|
840
|
+
if whole = parsed_manifest.dig('items', 0, 'metadata', 'sha256-whole')
|
841
|
+
[whole, CHECKSUM_HASH_TYPE_SHA256_MDM_DEPLOY]
|
842
|
+
elsif whole = parsed_manifest.dig('items', 0, 'metadata', 'md5-whole')
|
843
|
+
[whole, CHECKSUM_HASH_TYPE_MD5]
|
844
|
+
else
|
845
|
+
raise Jamf::MissingDataError, 'No whole-file checksum in the manifest. Must have either sha256-whole or md5-whole in the metadata'
|
846
|
+
end
|
847
|
+
end
|
848
|
+
private :manifest_checksum_for_deployment
|
849
|
+
|
850
|
+
# size in bytes is required for MDM deployments
|
851
|
+
# @return [Integer] the size in bytes in the manifest for MDM deployments
|
852
|
+
#####################################
|
853
|
+
def manifest_size_for_deployment(parsed_manifest)
|
854
|
+
size = parsed_manifest.dig 'items', 0, 'metadata', 'sizeInBytes'
|
855
|
+
raise Jamf::MissingDataError, 'No sizeInBytes in the manifest metadata' unless size
|
856
|
+
|
857
|
+
size
|
858
|
+
end
|
859
|
+
private :manifest_size_for_deployment
|
860
|
+
|
861
|
+
# bundle identifier is required for MDM deployments
|
862
|
+
# @return [String] the bundle identifier in the manifest for MDM deployments
|
863
|
+
#####################################
|
864
|
+
def manifest_bundle_identifier_for_deployment(parsed_manifest)
|
865
|
+
bid = parsed_manifest.dig 'items', 0, 'metadata', 'bundle-identifier'
|
866
|
+
raise Jamf::MissingDataError, 'No bundle-identifier in the manifest metadata' unless bid
|
867
|
+
|
868
|
+
bid
|
869
|
+
end
|
870
|
+
private :manifest_bundle_identifier_for_deployment
|
871
|
+
|
872
|
+
# bundle version is required for MDM deployments
|
873
|
+
# @return [String] the bundle version in the manifest for MDM deployments
|
874
|
+
#####################################
|
875
|
+
def manifest_bundle_version_for_deployment(parsed_manifest)
|
876
|
+
bv = parsed_manifest.dig 'items', 0, 'metadata', 'bundle-version'
|
877
|
+
raise Jamf::MissingDataError, 'No bundle-version in the manifest metadata' unless bv
|
878
|
+
|
879
|
+
bv
|
880
|
+
end
|
881
|
+
private :manifest_bundle_version_for_deployment
|
882
|
+
|
883
|
+
# title is required for MDM deployments
|
884
|
+
# @return [String] the title in the manifest for MDM deployments
|
885
|
+
#####################################
|
886
|
+
def manifest_title_for_deployment(parsed_manifest)
|
887
|
+
ttl = parsed_manifest.dig 'items', 0, 'metadata', 'title'
|
888
|
+
raise Jamf::MissingDataError, 'No title in the manifest metadata' unless ttl
|
889
|
+
|
890
|
+
ttl
|
891
|
+
end
|
892
|
+
private :manifest_title_for_deployment
|
893
|
+
|
894
|
+
# set the optional values for the MDM deployment manifest
|
895
|
+
# @return [void]
|
896
|
+
#####################################
|
897
|
+
def set_optional_mdm_manifest_values(parsed_manifest, mdm_manifest)
|
898
|
+
# subtitle is optional for MDM deployments
|
899
|
+
sttl = parsed_manifest.dig 'items', 0, 'metadata', 'subtitle'
|
900
|
+
mdm_manifest['subtitle'] = sttl if sttl
|
901
|
+
|
902
|
+
# Images are optional for MDM deployments
|
903
|
+
parsed_manifest['items'][0]['assets']&.each do |asset|
|
904
|
+
mdm_manifest['fullSizeImageURL'] = asset['url'] if asset['kind'] == 'full-size-image'
|
905
|
+
mdm_manifest['displayImageURL'] = asset['url'] if asset['kind'] == 'display-image'
|
906
|
+
end
|
907
|
+
end
|
908
|
+
private :set_optional_mdm_manifest_values
|
909
|
+
|
722
910
|
end # class
|
723
911
|
|
724
912
|
end # module
|