ruby-jss 1.3.2 → 1.5.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +122 -0
- data/lib/jamf.rb +18 -16
- data/lib/jamf/api/base_classes/collection_resource.rb +613 -0
- data/lib/jamf/api/{abstract_classes → base_classes}/json_object.rb +109 -101
- data/lib/jamf/api/{abstract_classes → base_classes}/prestage.rb +55 -30
- data/lib/jamf/api/{abstract_classes → base_classes}/resource.rb +10 -6
- data/lib/jamf/api/{abstract_classes → base_classes}/singleton_resource.rb +4 -3
- data/lib/jamf/api/connection.rb +13 -9
- data/lib/jamf/api/connection/api_error.rb +8 -8
- data/lib/jamf/api/connection/token.rb +36 -15
- data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
- data/lib/jamf/api/json_objects/{location.rb → device_enrollment_device_sync_state.rb} +27 -41
- data/lib/jamf/api/json_objects/device_enrollment_sync_status.rb +1 -1
- data/lib/jamf/api/json_objects/{attachment.rb → locale.rb} +14 -23
- data/lib/jamf/api/json_objects/md_prestage_name.rb +1 -1
- data/lib/jamf/api/json_objects/md_prestage_names.rb +2 -2
- data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
- data/lib/jamf/api/json_objects/prestage_assignment.rb +2 -2
- data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
- data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
- data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
- data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
- data/lib/jamf/api/mixins/{abstract.rb → base_class.rb} +34 -16
- data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
- data/lib/jamf/api/mixins/change_log.rb +201 -51
- data/lib/jamf/api/{resources/collection_resources/computer.rb → mixins/filterable.rb} +19 -17
- data/lib/jamf/api/mixins/pageable.rb +208 -0
- data/lib/jamf/api/{json_objects/installed_application.rb → mixins/sortable.rb} +33 -33
- data/lib/jamf/api/resources/collection_resources/building.rb +16 -9
- data/lib/jamf/api/resources/collection_resources/category.rb +5 -4
- data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +12 -5
- data/lib/jamf/api/resources/collection_resources/department.rb +1 -3
- data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +13 -13
- data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
- data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +25 -23
- data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
- data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
- data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
- data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
- data/lib/jamf/validate.rb +63 -24
- data/lib/jamf/version.rb +1 -1
- data/lib/jss.rb +2 -1
- data/lib/jss/api_connection.rb +113 -406
- data/lib/jss/api_object.rb +10 -20
- data/lib/jss/api_object/advanced_search.rb +27 -26
- data/lib/jss/api_object/app_store_country_codes.rb +298 -0
- data/lib/jss/api_object/categorizable.rb +1 -1
- data/lib/jss/api_object/computer.rb +13 -0
- data/lib/jss/api_object/configuration_profile.rb +60 -4
- data/lib/jss/api_object/directory_binding_type.rb +66 -60
- data/lib/jss/api_object/directory_binding_type/active_directory.rb +71 -34
- data/lib/jss/api_object/directory_binding_type/admitmac.rb +536 -467
- data/lib/jss/api_object/directory_binding_type/centrify.rb +21 -7
- data/lib/jss/api_object/directory_binding_type/open_directory.rb +4 -4
- data/lib/jss/api_object/distribution_point.rb +2 -2
- data/lib/jss/api_object/dock_item.rb +102 -96
- data/lib/jss/api_object/ebook.rb +1 -2
- data/lib/jss/api_object/extendable.rb +1 -1
- data/lib/jss/api_object/extension_attribute.rb +4 -3
- data/lib/jss/api_object/group.rb +33 -2
- data/lib/jss/api_object/mac_application.rb +107 -8
- data/lib/jss/api_object/network_segment.rb +43 -12
- data/lib/jss/api_object/package.rb +1 -1
- data/lib/jss/api_object/patch_source.rb +10 -9
- data/lib/jss/api_object/policy.rb +217 -28
- data/lib/jss/api_object/printer.rb +10 -4
- data/lib/jss/api_object/scopable.rb +10 -15
- data/lib/jss/api_object/scopable/scope.rb +389 -73
- data/lib/jss/api_object/self_servable.rb +17 -9
- data/lib/jss/api_object/uploadable.rb +1 -1
- data/lib/jss/api_object/user.rb +42 -1
- data/lib/jss/api_object/vpp_account.rb +209 -0
- data/lib/jss/api_object/vppable.rb +169 -13
- data/lib/jss/exceptions.rb +3 -0
- data/lib/jss/server.rb +15 -0
- data/lib/jss/utility.rb +142 -37
- data/lib/jss/validate.rb +53 -10
- data/lib/jss/version.rb +1 -1
- metadata +45 -61
- data/lib/jamf/api/abstract_classes/advanced_search.rb +0 -86
- data/lib/jamf/api/abstract_classes/collection_resource.rb +0 -433
- data/lib/jamf/api/abstract_classes/generic_reference.rb +0 -145
- data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +0 -126
- data/lib/jamf/api/json_objects/account_prefs.rb +0 -79
- data/lib/jamf/api/json_objects/android_details.rb +0 -139
- data/lib/jamf/api/json_objects/appletv_details.rb +0 -110
- data/lib/jamf/api/json_objects/cellular_network.rb +0 -151
- data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +0 -67
- data/lib/jamf/api/json_objects/criterion.rb +0 -152
- data/lib/jamf/api/json_objects/extension_attribute_value.rb +0 -128
- data/lib/jamf/api/json_objects/installed_certificate.rb +0 -53
- data/lib/jamf/api/json_objects/installed_configuration_profile.rb +0 -67
- data/lib/jamf/api/json_objects/installed_ebook.rb +0 -58
- data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +0 -59
- data/lib/jamf/api/json_objects/ios_details.rb +0 -244
- data/lib/jamf/api/json_objects/mobile_device_details.rb +0 -219
- data/lib/jamf/api/json_objects/mobile_device_security.rb +0 -101
- data/lib/jamf/api/json_objects/purchasing_data.rb +0 -125
- data/lib/jamf/api/mixins/locatable.rb +0 -124
- data/lib/jamf/api/mixins/referable.rb +0 -92
- data/lib/jamf/api/resources/collection_resources/account.rb +0 -163
- data/lib/jamf/api/resources/collection_resources/advanced_mobile_device_search.rb +0 -52
- data/lib/jamf/api/resources/collection_resources/advanced_user_search.rb +0 -52
- data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +0 -45
- data/lib/jamf/api/resources/collection_resources/mobile_device.rb +0 -315
- data/lib/jamf/api/resources/collection_resources/site.rb +0 -77
- data/lib/jamf/api/resources/singleton_resources/authorization.rb +0 -88
- data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +0 -139
- data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +0 -95
@@ -376,9 +376,9 @@ module JSS
|
|
376
376
|
#
|
377
377
|
# @author Tyler Morgan
|
378
378
|
#
|
379
|
-
# @param newvalue [String]
|
379
|
+
# @param newvalue [String, Float, Array[String], Array[Float]]
|
380
380
|
#
|
381
|
-
# @raise [JSS::InvalidDataError] If newvalue is not a String
|
381
|
+
# @raise [JSS::InvalidDataError] If newvalue is not a String, Float, Array containing Strings, or Array containing Floats.
|
382
382
|
#
|
383
383
|
# @example Limit Printer object to only High Sierra devices and Mojave 10.14.5 OS versions
|
384
384
|
# printer.os_requirements = "10.13.x, 10.14.5"
|
@@ -386,8 +386,14 @@ module JSS
|
|
386
386
|
# @return [void]
|
387
387
|
def os_requirements=(newvalue)
|
388
388
|
|
389
|
-
|
390
|
-
|
389
|
+
if newvalue.is_a? Array
|
390
|
+
# Parse Array
|
391
|
+
raise JSS::InvalidDataError, "If setting os_requirements with an array, it must contain strings or floats." unless newvalue[0].is_a?(String) || newvalue[0].is_a?(Float)
|
392
|
+
newvalue = newvalue.map { |x| x.to_s }.join(',')
|
393
|
+
else
|
394
|
+
raise JSS::InvalidDataError, "os_requirements must either be a string, float, or an array containing strings or floats." unless (newvalue.is_a?(String) || newvalue.is_a?(Float)) && !newvalue.nil?
|
395
|
+
end
|
396
|
+
|
391
397
|
@os_requirements = newvalue
|
392
398
|
|
393
399
|
@need_to_update = true
|
@@ -100,22 +100,17 @@ module JSS
|
|
100
100
|
@need_to_update = true if @in_jss
|
101
101
|
end
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
###
|
103
|
+
# A wrapper around the update method, to try catching 409 conflict errors
|
104
|
+
# when we couldn't verify all ldap users/groups due to lack of ldap connections
|
105
|
+
#
|
107
106
|
def update
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
raise conflict
|
116
|
-
end
|
117
|
-
|
118
|
-
end # begin
|
107
|
+
super
|
108
|
+
rescue JSS::ConflictError => conflict
|
109
|
+
if scope.unable_to_verify_ldap_entries == true
|
110
|
+
raise JSS::InvalidDataError, "Potentially non-existant LDAP user or group in new scope values."
|
111
|
+
else
|
112
|
+
raise conflict
|
113
|
+
end
|
119
114
|
end # update
|
120
115
|
|
121
116
|
end # module Scopable
|
@@ -41,17 +41,21 @@ module JSS
|
|
41
41
|
# This class provides methods for adding, removing, or fully replacing the
|
42
42
|
# various items in scope's realms: targets, limitations, and exclusions.
|
43
43
|
#
|
44
|
-
#
|
44
|
+
# This class also provides a way to see if a machine will be included in
|
45
|
+
# this scope.
|
46
|
+
#
|
47
|
+
# IMPORTANT - Users & User Groups in Targets and Exclusions:
|
48
|
+
#
|
45
49
|
# The classic API has bugs regarding the use of Users, UserGroups,
|
46
50
|
# LDAP/Local Users, & LDAP User Groups in scopes. Here's a discussion
|
47
51
|
# of those bugs and how ruby-jss handles them.
|
48
52
|
#
|
49
53
|
# Targets/Inclusions
|
50
|
-
# - 'Users' can only be JSS::Users - No LDAP
|
54
|
+
# - 'Users' in the Scope UI can only be JSS::Users - No LDAP
|
51
55
|
# - BUG: They do not appear in API data (XML or JSON) and are
|
52
56
|
# NOT SUPPORTED in ruby-jss.
|
53
57
|
# - You must use the Web UI to work with them in a Scope.
|
54
|
-
# - 'User Groups' can only be JSS::UserGroups - No LDAP
|
58
|
+
# - 'User Groups' in the Scope UI can only be JSS::UserGroups - No LDAP
|
55
59
|
# - BUG: They do not appear in API data (XML or JSON) and are
|
56
60
|
# NOT SUPPORTED in ruby-jss.
|
57
61
|
# - You must use the Web UI to work with them in a Scope.
|
@@ -70,11 +74,11 @@ module JSS
|
|
70
74
|
# scope=>limitations=>user_groups
|
71
75
|
#
|
72
76
|
# Exclusions, combines the behavior of Inclusions & Limitations
|
73
|
-
# - 'Users' can only be JSS::Users - No LDAP
|
77
|
+
# - 'Users' in the Scope UI can only be JSS::Users - No LDAP
|
74
78
|
# - BUG: They do not appear in API data (XML or JSON) and are
|
75
79
|
# NOT SUPPORTED in ruby-jss.
|
76
80
|
# - You must use the Web UI to work with them in a Scope.
|
77
|
-
# - 'User Groups' can only be JSS::UserGroups - No LDAP
|
81
|
+
# - 'User Groups' in the Scope UI can only be JSS::UserGroups - No LDAP
|
78
82
|
# - BUG: They do not appear in API data (XML or JSON) and are
|
79
83
|
# NOT SUPPORTED in ruby-jss.
|
80
84
|
# - You must use the Web UI to work with them in a Scope.
|
@@ -220,62 +224,70 @@ module JSS
|
|
220
224
|
# A reference to the object that contains this Scope
|
221
225
|
#
|
222
226
|
# For telling it when a change is made and an update needed
|
227
|
+
# and for accessing its api connection
|
223
228
|
attr_accessor :container
|
224
229
|
|
225
230
|
# @return [Boolean] should we expect a potential 409 Conflict
|
226
231
|
# if we can't connect to LDAP servers for verification?
|
227
232
|
attr_accessor :unable_to_verify_ldap_entries
|
228
233
|
|
229
|
-
# what type of target is this scope for? Computers or
|
234
|
+
# what type of target is this scope for? Computers or MobileDevices?
|
230
235
|
attr_reader :target_class
|
231
236
|
|
232
|
-
#
|
233
|
-
|
234
|
-
# The items which form the base scope of included targets
|
235
|
-
#
|
236
|
-
# This is the group of targets to which the limitations and exclusions apply.
|
237
|
-
# they keys are:
|
238
|
-
# - :targets
|
239
|
-
# - :target_groups
|
240
|
-
# - :departments
|
241
|
-
# - :buildings
|
242
|
-
# and the values are Arrays of names of those things.
|
243
|
-
#
|
244
|
-
attr_reader :inclusions
|
237
|
+
# what type of target group is this scope for? ComputerGroups or MobileDeviceGroups?
|
238
|
+
attr_reader :group_class
|
245
239
|
|
246
240
|
# @return [Boolean]
|
247
241
|
#
|
248
242
|
# Does this scope cover all targets?
|
249
243
|
#
|
250
|
-
# If this is true, the @
|
244
|
+
# If this is true, the @targets Hash is ignored, and all
|
251
245
|
# targets in the JSS form the base scope.
|
252
246
|
#
|
253
247
|
attr_reader :all_targets
|
248
|
+
alias all_targets? all_targets
|
254
249
|
|
255
|
-
#
|
250
|
+
# The items which form the base scope of included targets
|
256
251
|
#
|
257
|
-
#
|
252
|
+
# This is the group of targets to which the limitations and exclusions apply.
|
253
|
+
# they keys are:
|
254
|
+
# - :computers or :mobile_devices (which are directly targeted)
|
255
|
+
# - :direct_targets - a synonym for :mobile_devices or :computers
|
256
|
+
# - :computer_groups or :mobile_device_groups (which target all of their memebers)
|
257
|
+
# - :group_targets - a synonym for :computer_groups or :mobile_device_groups
|
258
|
+
# - :departments
|
259
|
+
# - :buildings
|
260
|
+
# and the values are Arrays of names of those things.
|
258
261
|
#
|
259
|
-
#
|
262
|
+
# @return [Hash{Symbol: Array<Integer>}]
|
263
|
+
attr_reader :targets
|
264
|
+
# backward compatibility
|
265
|
+
alias inclusions targets
|
266
|
+
|
267
|
+
# The items in these arrays are the limitations applied to targets in the @targets .
|
268
|
+
#
|
269
|
+
# The arrays of ids are:
|
260
270
|
# - :network_segments
|
261
|
-
# - :
|
271
|
+
# - :jamf_ldap_users
|
262
272
|
# - :user_groups
|
263
273
|
#
|
274
|
+
# @return [Hash{Symbol: Array<Integer, String>}]
|
264
275
|
attr_reader :limitations
|
265
276
|
|
266
|
-
# @
|
277
|
+
# The items in these arrays are the exclusions applied to targets in the @targets .
|
267
278
|
#
|
268
|
-
# The
|
269
|
-
#
|
270
|
-
#
|
271
|
-
# - :
|
272
|
-
# - :
|
279
|
+
# The arrays of ids are:
|
280
|
+
# - :computers or :mobile_devices (which are directly excluded)
|
281
|
+
# - :direct_exclusions - a synonym for :mobile_devices or :computers
|
282
|
+
# - :computer_groups or :mobile_device_groups (which exclude all of their memebers)
|
283
|
+
# - :group_exclusions - a synonym for :computer_groups or :mobile_device_groups
|
273
284
|
# - :departments
|
274
285
|
# - :buildings
|
275
286
|
# - :network_segments
|
276
287
|
# - :users
|
277
288
|
# - :user_groups
|
278
289
|
#
|
290
|
+
# @return [Hash{Symbol: Array<Integer, String>}]
|
279
291
|
attr_reader :exclusions
|
280
292
|
|
281
293
|
# Public Instance Methods
|
@@ -299,7 +311,7 @@ module JSS
|
|
299
311
|
@group_key = TARGETS_AND_GROUPS[@target_key]
|
300
312
|
@group_class = SCOPING_CLASSES[@group_key]
|
301
313
|
|
302
|
-
@
|
314
|
+
@target_keys = [@target_key, @group_key] + INCLUSIONS
|
303
315
|
@exclusion_keys = [@target_key, @group_key] + EXCLUSIONS
|
304
316
|
|
305
317
|
@all_key = "all_#{target_key}".to_sym
|
@@ -307,11 +319,13 @@ module JSS
|
|
307
319
|
|
308
320
|
# Everything gets mapped from an Array of Hashes to
|
309
321
|
# an Array of ids
|
310
|
-
@
|
311
|
-
@
|
322
|
+
@targets = {}
|
323
|
+
@target_keys.each do |k|
|
312
324
|
raw_scope[k] ||= []
|
313
|
-
@
|
314
|
-
|
325
|
+
@targets[k] = raw_scope[k].compact.map { |n| n[:id].to_i }
|
326
|
+
@targets[:direct_targets] = @targets[k] if k == @target_key
|
327
|
+
@targets[:group_targets] = @targets[k] if k == @group_key
|
328
|
+
end # @target_keys.each do |k|
|
315
329
|
|
316
330
|
# the :users key from the API is what we call :jamf_ldap_users
|
317
331
|
# and the :user_groups key from the API we call :ldap_user_groups
|
@@ -365,6 +379,8 @@ module JSS
|
|
365
379
|
api_data = raw_scope[:exclusions][k]
|
366
380
|
api_data ||= []
|
367
381
|
@exclusions[k] = api_data.compact.map { |n| n[:id].to_i }
|
382
|
+
@exclusions[:direct_exclusions] = @exclusions[k] if k == @target_key
|
383
|
+
@exclusions[:group_exclusions] = @exclusions[k] if k == @group_key
|
368
384
|
end # if ...elsif... else
|
369
385
|
end # @exclusion_keys.each
|
370
386
|
end # if raw_scope[:exclusions]
|
@@ -382,8 +398,8 @@ module JSS
|
|
382
398
|
# @return [void]
|
383
399
|
#
|
384
400
|
def include_all(clear = false)
|
385
|
-
@
|
386
|
-
@
|
401
|
+
@targets = {}
|
402
|
+
@target_keys.each { |k| @targets[k] = [] }
|
387
403
|
@all_targets = true
|
388
404
|
if clear
|
389
405
|
@limitations = {}
|
@@ -392,7 +408,7 @@ module JSS
|
|
392
408
|
@exclusions = {}
|
393
409
|
@exclusion_keys.each { |k| @exclusions[k] = [] }
|
394
410
|
end
|
395
|
-
@container
|
411
|
+
@container&.should_update
|
396
412
|
end
|
397
413
|
|
398
414
|
# Replace a list of item names for as targets in this scope.
|
@@ -420,7 +436,7 @@ module JSS
|
|
420
436
|
list.map! do |ident|
|
421
437
|
item_id = validate_item(:target, key, ident)
|
422
438
|
|
423
|
-
if @exclusions[key]
|
439
|
+
if @exclusions[key]&.include?(item_id)
|
424
440
|
raise JSS::AlreadyExistsError, \
|
425
441
|
"Can't set #{key} target to '#{ident}' because it's already an explicit exclusion."
|
426
442
|
end
|
@@ -428,11 +444,11 @@ module JSS
|
|
428
444
|
item_id
|
429
445
|
end # each
|
430
446
|
|
431
|
-
return nil if list.sort == @
|
447
|
+
return nil if list.sort == @targets[key].sort
|
432
448
|
|
433
|
-
@
|
449
|
+
@targets[key] = list
|
434
450
|
@all_targets = false
|
435
|
-
@container
|
451
|
+
@container&.should_update
|
436
452
|
end # sinclude_in_scope
|
437
453
|
alias set_target set_targets
|
438
454
|
alias set_inclusion set_targets
|
@@ -458,13 +474,13 @@ module JSS
|
|
458
474
|
def add_target(key, item)
|
459
475
|
key = pluralize_key(key)
|
460
476
|
item_id = validate_item(:target, key, item)
|
461
|
-
return if @
|
477
|
+
return if @targets[key]&.include?(item_id)
|
462
478
|
|
463
|
-
raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key]
|
479
|
+
raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key]&.include?(item_id)
|
464
480
|
|
465
|
-
@
|
481
|
+
@targets[key] << item_id
|
466
482
|
@all_targets = false
|
467
|
-
@container
|
483
|
+
@container&.should_update
|
468
484
|
end
|
469
485
|
alias add_inclusion add_target
|
470
486
|
|
@@ -483,10 +499,10 @@ module JSS
|
|
483
499
|
key = pluralize_key(key)
|
484
500
|
item_id = validate_item :target, key, item, error_if_not_found: false
|
485
501
|
return unless item_id
|
486
|
-
return unless @
|
502
|
+
return unless @targets[key]&.include?(item_id)
|
487
503
|
|
488
|
-
@
|
489
|
-
@container
|
504
|
+
@targets[key].delete item_id
|
505
|
+
@container&.should_update
|
490
506
|
end
|
491
507
|
alias remove_inclusion remove_target
|
492
508
|
|
@@ -513,7 +529,7 @@ module JSS
|
|
513
529
|
# check the idents
|
514
530
|
list.map! do |ident|
|
515
531
|
item_id = validate_item(:limitation, key, ident)
|
516
|
-
if @exclusions[key]
|
532
|
+
if @exclusions[key]&.include?(item_id)
|
517
533
|
raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion."
|
518
534
|
end
|
519
535
|
|
@@ -523,7 +539,7 @@ module JSS
|
|
523
539
|
return nil if list.sort == @limitations[key].sort
|
524
540
|
|
525
541
|
@limitations[key] = list
|
526
|
-
@container
|
542
|
+
@container&.should_update
|
527
543
|
end # set_limitation
|
528
544
|
alias set_limitations set_limitation
|
529
545
|
|
@@ -545,14 +561,14 @@ module JSS
|
|
545
561
|
def add_limitation(key, item)
|
546
562
|
key = pluralize_key(key)
|
547
563
|
item_id = validate_item(:limitation, key, item)
|
548
|
-
return nil if @limitations[key]
|
564
|
+
return nil if @limitations[key]&.include?(item_id)
|
549
565
|
|
550
|
-
if @exclusions[key]
|
566
|
+
if @exclusions[key]&.include?(item_id)
|
551
567
|
raise JSS::AlreadyExistsError, "Can't set #{key} limitation for '#{name}' because it's already an explicit exclusion."
|
552
568
|
end
|
553
569
|
|
554
570
|
@limitations[key] << item_id
|
555
|
-
@container
|
571
|
+
@container&.should_update
|
556
572
|
end
|
557
573
|
|
558
574
|
# Remove a single item for limiting this scope.
|
@@ -572,10 +588,10 @@ module JSS
|
|
572
588
|
key = pluralize_key(key)
|
573
589
|
item_id = validate_item :limitation, key, item, error_if_not_found: false
|
574
590
|
return unless item_id
|
575
|
-
return unless @limitations[key]
|
591
|
+
return unless @limitations[key]&.include?(item_id)
|
576
592
|
|
577
593
|
@limitations[key].delete item_id
|
578
|
-
@container
|
594
|
+
@container&.should_update
|
579
595
|
end ###
|
580
596
|
|
581
597
|
# Replace an exclusion list for this scope
|
@@ -600,8 +616,10 @@ module JSS
|
|
600
616
|
list.map! do |ident|
|
601
617
|
item_id = validate_item(:exclusion, key, ident)
|
602
618
|
case key
|
603
|
-
when *@
|
604
|
-
|
619
|
+
when *@target_keys
|
620
|
+
if @targets[key] && @exclusions[key].include?(item_id)
|
621
|
+
raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included."
|
622
|
+
end
|
605
623
|
when *LIMITATIONS
|
606
624
|
if @limitations[key] && @exclusions[key].include?(item_id)
|
607
625
|
raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already an explicit limitation."
|
@@ -613,7 +631,7 @@ module JSS
|
|
613
631
|
return nil if list.sort == @exclusions[key].sort
|
614
632
|
|
615
633
|
@exclusions[key] = list
|
616
|
-
@container
|
634
|
+
@container&.should_update
|
617
635
|
end # limit scope
|
618
636
|
|
619
637
|
# Add a single item for exclusions of this scope.
|
@@ -632,14 +650,14 @@ module JSS
|
|
632
650
|
def add_exclusion(key, item)
|
633
651
|
key = pluralize_key(key)
|
634
652
|
item_id = validate_item(:exclusion, key, item)
|
635
|
-
return if @exclusions[key]
|
653
|
+
return if @exclusions[key]&.include?(item_id)
|
636
654
|
|
637
|
-
raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @
|
655
|
+
raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @targets[key]&.include?(item)
|
638
656
|
|
639
|
-
raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]
|
657
|
+
raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]&.include?(item)
|
640
658
|
|
641
659
|
@exclusions[key] << item_id
|
642
|
-
@container
|
660
|
+
@container&.should_update
|
643
661
|
end
|
644
662
|
|
645
663
|
# Remove a single item for exclusions of this scope
|
@@ -656,10 +674,10 @@ module JSS
|
|
656
674
|
def remove_exclusion(key, item)
|
657
675
|
key = pluralize_key(key)
|
658
676
|
item_id = validate_item :exclusion, key, item, error_if_not_found: false
|
659
|
-
return unless @exclusions[key]
|
677
|
+
return unless @exclusions[key]&.include?(item_id)
|
660
678
|
|
661
679
|
@exclusions[key].delete item_id
|
662
|
-
@container
|
680
|
+
@container&.should_update
|
663
681
|
end
|
664
682
|
|
665
683
|
# @api private
|
@@ -672,7 +690,8 @@ module JSS
|
|
672
690
|
scope = REXML::Element.new 'scope'
|
673
691
|
scope.add_element(@all_key.to_s).text = @all_targets
|
674
692
|
|
675
|
-
@
|
693
|
+
@target_keys.each do |klass|
|
694
|
+
list = @targets[klass]
|
676
695
|
list.compact!
|
677
696
|
list.delete 0
|
678
697
|
list_as_hashes = list.map { |i| { id: i } }
|
@@ -702,7 +721,8 @@ module JSS
|
|
702
721
|
end
|
703
722
|
|
704
723
|
exclusions = scope.add_element('exclusions')
|
705
|
-
@
|
724
|
+
@exclusion_keys.each do |klass|
|
725
|
+
list = @exclusions[klass]
|
706
726
|
list.compact!
|
707
727
|
list.delete 0
|
708
728
|
if klass == :jamf_ldap_users
|
@@ -737,9 +757,59 @@ module JSS
|
|
737
757
|
vars
|
738
758
|
end
|
739
759
|
|
740
|
-
#
|
760
|
+
# Return a hash of id => name for all machines in the target class
|
761
|
+
# that are within this scope.
|
762
|
+
#
|
763
|
+
# WARNING: This must instantiate all machines in the target class.
|
764
|
+
# It will still be slow, at least the first time for each target class.
|
765
|
+
# On the upside, the instantiated machines will be cached, so generating
|
766
|
+
# this list for other scopes with the same target class will be much
|
767
|
+
# much faster.
|
768
|
+
# In tests, 1600 Computers took about 7 minutes the first time,
|
769
|
+
# but less than 1 second after caching.
|
770
|
+
#
|
771
|
+
# See also the warning for #in_scope?
|
772
|
+
#
|
773
|
+
# @return [Hash{Integer => String}]
|
774
|
+
#
|
775
|
+
################
|
776
|
+
def scoped_machines
|
777
|
+
scoped_machines = {}
|
778
|
+
@target_class.all_objects(api: container.api).each do |machine|
|
779
|
+
scoped_machines[machine.id] = machine.name if in_scope? machine
|
780
|
+
end
|
781
|
+
scoped_machines
|
782
|
+
end
|
783
|
+
|
784
|
+
# is a given machine is in this scope?
|
785
|
+
#
|
786
|
+
# For a parameter you may pass either an instantiated
|
787
|
+
# JSS::MobileDevice or JSS::Computer, or an identifier for one.
|
788
|
+
# If an identifier is passed, it is not instantiated, but an API
|
789
|
+
# request is made for just the required subsets of data, thus
|
790
|
+
# speeding things up a bit when calling this method many times.
|
791
|
+
#
|
792
|
+
# WARNING: For scopes that include Jamf Users and Jamf User Groups
|
793
|
+
# as targets or exclusions, this method may return an incorrect value.
|
794
|
+
# See the discussion in the documentation for the Scopable::Scope class
|
795
|
+
# under 'IMPORTANT - Users & User Groups in Targets and Exclusions'
|
796
|
+
#
|
797
|
+
# NOTE: currently in-range iBeacons are transient, and are not reported
|
798
|
+
# to the JSS as inventory data. As such they are ignored in this result.
|
799
|
+
# If a scope contains iBeacon limitations or exclusions, it is up to
|
800
|
+
# the user to be aware of that when evaluating the meaning of this result.
|
801
|
+
#
|
802
|
+
# @param machine[Integer, String, JSS::MobileDevice, JSS::Computer]
|
803
|
+
# Either an identifier for the machine, or an instantiated object
|
804
|
+
#
|
805
|
+
# @return [Boolean]
|
806
|
+
#
|
807
|
+
##############
|
808
|
+
def in_scope?(machine)
|
809
|
+
machine_data = fetch_machine_data machine
|
741
810
|
|
742
|
-
|
811
|
+
a_target?(machine_data) && within_limitations?(machine_data) && !excluded?(machine_data)
|
812
|
+
end
|
743
813
|
|
744
814
|
# Private Instance Methods
|
745
815
|
#####################################
|
@@ -765,7 +835,7 @@ module JSS
|
|
765
835
|
# which keys allowed depends on how the item is used...
|
766
836
|
possible_keys =
|
767
837
|
case realm
|
768
|
-
when :target then @
|
838
|
+
when :target then @target_keys
|
769
839
|
when :limitation then LIMITATIONS
|
770
840
|
when :exclusion then @exclusion_keys
|
771
841
|
else
|
@@ -781,15 +851,15 @@ module JSS
|
|
781
851
|
|
782
852
|
# id will be a string
|
783
853
|
if key == :jamf_ldap_users
|
784
|
-
id = ident if JSS::User.all_names(:refresh).include?(ident) || JSS::LDAPServer.user_in_ldap?(ident)
|
854
|
+
id = ident if JSS::User.all_names(:refresh, api: container.api).include?(ident) || JSS::LDAPServer.user_in_ldap?(ident)
|
785
855
|
|
786
856
|
# id will be a string
|
787
857
|
elsif key == :ldap_user_groups
|
788
|
-
id = ident if JSS::LDAPServer.group_in_ldap? ident
|
858
|
+
id = ident if JSS::LDAPServer.group_in_ldap? ident, api: container.api
|
789
859
|
|
790
860
|
# id will be an integer
|
791
861
|
else
|
792
|
-
id = SCOPING_CLASSES[key].valid_id ident
|
862
|
+
id = SCOPING_CLASSES[key].valid_id ident, api: container.api
|
793
863
|
end
|
794
864
|
|
795
865
|
raise JSS::NoSuchItemError, "No existing #{key} matching '#{ident}'" if error_if_not_found && id.nil?
|
@@ -809,6 +879,252 @@ module JSS
|
|
809
879
|
end
|
810
880
|
end
|
811
881
|
|
882
|
+
# The data used by the methods that figure out if a machine is
|
883
|
+
# in this scope, a Hash of Hashes. the sub hashes are:
|
884
|
+
#
|
885
|
+
# general: the 'general' subset
|
886
|
+
# location: the 'location' subset
|
887
|
+
# group_ids: an Array of the group ids to which the machine belongs.
|
888
|
+
#
|
889
|
+
# @param machine[Integer, String, JSS::MobileDevice, JSS::Computer]
|
890
|
+
# Either an identifier for the machine, or an instantiated object
|
891
|
+
#
|
892
|
+
# @return
|
893
|
+
#
|
894
|
+
def fetch_machine_data(machine)
|
895
|
+
case machine
|
896
|
+
when JSS::Computer
|
897
|
+
raise JSS::InvalidDataError, "Targets of this scope must be #{@target_class}" unless @target_class == JSS::Computer
|
898
|
+
|
899
|
+
general = machine.init_data[:general]
|
900
|
+
location = machine.init_data[:location]
|
901
|
+
group_ids = group_ids machine.computer_groups
|
902
|
+
|
903
|
+
# put in standardize place for easier use
|
904
|
+
# MDevs already have this at general[:managed]
|
905
|
+
general[:managed] = general[:remote_management][:managed]
|
906
|
+
|
907
|
+
when JSS::MobileDevice
|
908
|
+
raise JSS::InvalidDataError, "Targets of this scope must be #{@target_class}" unless @target_class == JSS::MobileDevice
|
909
|
+
|
910
|
+
general = machine.init_data[:general]
|
911
|
+
location = machine.init_data[:location]
|
912
|
+
group_ids = group_ids machine.mobile_device_groups
|
913
|
+
|
914
|
+
else
|
915
|
+
general, location, group_ids = fetch_subsets(machine)
|
916
|
+
end # case
|
917
|
+
|
918
|
+
{
|
919
|
+
general: general,
|
920
|
+
location: location,
|
921
|
+
group_ids: group_ids
|
922
|
+
}
|
923
|
+
end
|
924
|
+
|
925
|
+
# When we are given an indentifier for a machine,
|
926
|
+
# fetch just the subsets of API data we need to
|
927
|
+
# determine if the machine is in this scope
|
928
|
+
#
|
929
|
+
# @param ident[String, Integer]
|
930
|
+
#
|
931
|
+
# @return [Array] the general, locacation, and parsed group IDs
|
932
|
+
#
|
933
|
+
def fetch_subsets(ident)
|
934
|
+
id = @target_class.valid_id ident, api: container.api
|
935
|
+
raise JSS::NoSuchItemError, "No #{@target_class} matching #{machine}" unless id
|
936
|
+
|
937
|
+
if @target_class == JSS::MobileDevice
|
938
|
+
grp_subset = 'MobileDeviceGroups'
|
939
|
+
top_key = :mobile_device
|
940
|
+
else
|
941
|
+
grp_subset = 'GroupsAccounts'
|
942
|
+
top_key = :computer
|
943
|
+
end
|
944
|
+
subset_rsrc = "#{@target_class::RSRC_BASE}/id/#{id}/subset/General&Location&#{grp_subset}"
|
945
|
+
data = container.api.get_rsrc(subset_rsrc)[top_key]
|
946
|
+
grp_data =
|
947
|
+
if @target_class == JSS::MobileDevice
|
948
|
+
data[:mobile_device_groups]
|
949
|
+
else
|
950
|
+
data[:groups_accounts][:computer_group_memberships]
|
951
|
+
end
|
952
|
+
|
953
|
+
[data[:general], data[:location], group_ids(grp_data)]
|
954
|
+
end
|
955
|
+
|
956
|
+
# Given the raw API data for a machines group membership,
|
957
|
+
# return an array of the IDs of the groups.
|
958
|
+
#
|
959
|
+
# @param raw[Array] The API array of the machine's group memberships
|
960
|
+
#
|
961
|
+
# @return [Array] The ID's of the groups to which the machine belongs.
|
962
|
+
#
|
963
|
+
def group_ids(raw)
|
964
|
+
if @target_class == JSS::MobileDevice
|
965
|
+
raw.map { |mdg| mdg[:id] }
|
966
|
+
else
|
967
|
+
names_to_ids = @group_class.map_all_ids_to(:name).invert
|
968
|
+
raw.map { |gn| names_to_ids[gn] }
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
972
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
973
|
+
# @return [Boolean]
|
974
|
+
################
|
975
|
+
def a_target?(machine_data)
|
976
|
+
return false unless machine_data[:general][:managed]
|
977
|
+
return true if \
|
978
|
+
all_targets? || \
|
979
|
+
machine_directly_scoped?(machine_data, :target) || \
|
980
|
+
machine_in_scope_group?(machine_data, :target) || \
|
981
|
+
machine_in_scope_buildings?(machine_data, :target) || \
|
982
|
+
machine_in_scope_depts?(machine_data, :target)
|
983
|
+
|
984
|
+
false
|
985
|
+
end
|
986
|
+
|
987
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
988
|
+
# @return [Boolean]
|
989
|
+
################
|
990
|
+
def within_limitations?(machine_data)
|
991
|
+
return false if \
|
992
|
+
machine_in_scope_netsegs?(machine_data, :limitation) == false || \
|
993
|
+
machine_in_scope_jamf_ldap_users_list?(machine_data, :limitation) == false || \
|
994
|
+
machine_in_scope_ldap_usergroup_list?(machine_data, :limitation) == false
|
995
|
+
|
996
|
+
true
|
997
|
+
end
|
998
|
+
|
999
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1000
|
+
# @return [Boolean]
|
1001
|
+
################
|
1002
|
+
def excluded?(machine_data)
|
1003
|
+
return true if
|
1004
|
+
machine_directly_scoped?(machine_data, :exclusion) || \
|
1005
|
+
machine_in_scope_group?(machine_data, :exclusion) || \
|
1006
|
+
machine_in_scope_buildings?(machine_data, :exclusion) || \
|
1007
|
+
machine_in_scope_depts?(machine_data, :exclusion) || \
|
1008
|
+
machine_in_scope_netsegs?(machine_data, :exclusion) || \
|
1009
|
+
machine_in_scope_jamf_ldap_users_list?(machine_data, :exclusion) || \
|
1010
|
+
machine_in_scope_ldap_usergroup_list?(machine_data, :exclusion)
|
1011
|
+
|
1012
|
+
false
|
1013
|
+
end
|
1014
|
+
|
1015
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1016
|
+
# @param part[Symbol] either :target or :exclusion
|
1017
|
+
# @return [Boolean] Is the machine directly spcified in this part of the scope?
|
1018
|
+
################
|
1019
|
+
def machine_directly_scoped?(machine_data, part)
|
1020
|
+
scope_list = part == :target ? @targets[:direct_targets] : @exclusions[:direct_exclusions]
|
1021
|
+
scope_list.include? machine_data[:general][:id]
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1025
|
+
# @param part[Symbol] either :target or :exclusion
|
1026
|
+
# @return [Boolean] Is the machine a member of any group listed in this part of the scope?
|
1027
|
+
################
|
1028
|
+
def machine_in_scope_group?(machine_data, part)
|
1029
|
+
scope_list = part == :target ? @targets[:group_targets] : @exclusions[:group_exclusions]
|
1030
|
+
# if the list is empty, return nil
|
1031
|
+
return if scope_list.empty?
|
1032
|
+
|
1033
|
+
# if the intersection of the machine's group ids, and those of the scope part
|
1034
|
+
# is not empty, then the machine is in at least one of the groups
|
1035
|
+
!(machine_data[:group_ids] & scope_list).empty?
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1039
|
+
# @param part[Symbol] either :target or :exclusion
|
1040
|
+
# @return [Boolean] Is the machine in any building listed in this part of the scope?
|
1041
|
+
#################
|
1042
|
+
def machine_in_scope_buildings?(machine_data, part)
|
1043
|
+
scope_list = part == :target ? @targets[:buildings] : @exclusions[:buildings]
|
1044
|
+
|
1045
|
+
# nil if empty
|
1046
|
+
return if scope_list.empty?
|
1047
|
+
# false if no building for the machine - it isn't in any dept
|
1048
|
+
return false if machine_data[:location][:building].to_s.empty?
|
1049
|
+
|
1050
|
+
building_id = JSS::Building.map_all_ids_to(:name).invert[machine_data[:location][:building]]
|
1051
|
+
scope_list.include? building_id
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1055
|
+
# @param part[Symbol] either :target or :exclusion
|
1056
|
+
# @return [Boolean] Is the machine in any department listed in this part of the scope?
|
1057
|
+
#################
|
1058
|
+
def machine_in_scope_depts?(machine_data, part)
|
1059
|
+
scope_list = part == :target ? @targets[:departments] : @exclusions[:departments]
|
1060
|
+
|
1061
|
+
# nil if empty
|
1062
|
+
return if scope_list.empty?
|
1063
|
+
# false if no dept for the machine - it isn't in any dept
|
1064
|
+
return false if machine_data[:location][:department].to_s.empty?
|
1065
|
+
|
1066
|
+
dept_id = JSS::Department.map_all_ids_to(:name).invert[machine_data[:location][:department]]
|
1067
|
+
|
1068
|
+
scope_list.include? dept_id
|
1069
|
+
end
|
1070
|
+
|
1071
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1072
|
+
# @param part[Symbol] either :limitation or :exclusion
|
1073
|
+
# @return [Boolean] Is the machine in any NetworkSegment listed in this part of the scope?
|
1074
|
+
##################
|
1075
|
+
def machine_in_scope_netsegs?(machine_data, part)
|
1076
|
+
scope_list = part == :limitation ? @limitations[:network_segments] : @exclusions[:network_segments]
|
1077
|
+
|
1078
|
+
# nil if no netsegs in scope part
|
1079
|
+
return if scope_list.empty?
|
1080
|
+
|
1081
|
+
ip = @target_class == JSS::Computer ? machine_data[:general][:last_reported_ip] : machine_data[:general][:ip_address]
|
1082
|
+
# false if no ip for machine - it isn't in a any of the segs
|
1083
|
+
return false if ip.to_s.empty?
|
1084
|
+
|
1085
|
+
mach_segs = JSS::NetworkSegment.network_segments_for_ip ip
|
1086
|
+
|
1087
|
+
# if the intersection is not empty, then the machine is in at least one of the net segs
|
1088
|
+
!(mach_segs & scope_list).empty?
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1092
|
+
# @param part[Symbol] either :limitation or :exclusion
|
1093
|
+
# @return [Boolean] Is the user of this machine in the list of jamf/ldap users in this part of the scope?
|
1094
|
+
##################
|
1095
|
+
def machine_in_scope_jamf_ldap_users_list?(machine_data, part)
|
1096
|
+
scope_list = part == :limitation ? @limitations[:jamf_ldap_users] : @exclusions[:jamf_ldap_users]
|
1097
|
+
|
1098
|
+
# nil if the list is empty
|
1099
|
+
return if scope_list.empty?
|
1100
|
+
|
1101
|
+
scope_list.include? machine_data[:location][:username]
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
# @param machine_data[Hash] See #fetch_machine_data
|
1105
|
+
# @param part[Symbol] either :limitation or :exclusion
|
1106
|
+
# @return [Boolean] Is the user of this machine a member of any of the LDAP groups in in this part of the scope?
|
1107
|
+
##################
|
1108
|
+
def machine_in_scope_ldap_usergroup_list?(machine_data, part)
|
1109
|
+
scope_list = part == :limitation ? @limitations[:ldap_user_groups] : @exclusions[:ldap_user_groups]
|
1110
|
+
|
1111
|
+
# nil if the list is empty
|
1112
|
+
return if scope_list.empty?
|
1113
|
+
|
1114
|
+
# loop thru them checking to see if the user is a member
|
1115
|
+
scope_list.each do |ldapgroup|
|
1116
|
+
server = JSS::LDAPServer.server_for_group ldapgroup
|
1117
|
+
# if the group doesn't exist in any LDAP the user isn't a part of it
|
1118
|
+
next unless server
|
1119
|
+
|
1120
|
+
# if the user name is in any group, return true
|
1121
|
+
return true if JSS::LDAPServer.check_membership server, machine_data[:location][:username], ldapgroup
|
1122
|
+
end
|
1123
|
+
|
1124
|
+
# if we're here, not in any group
|
1125
|
+
false
|
1126
|
+
end
|
1127
|
+
|
812
1128
|
end # class Scope
|
813
1129
|
|
814
1130
|
end # module Scopable
|