ruby-jss 1.2.10 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +208 -1
  3. data/lib/jamf.rb +18 -16
  4. data/lib/jamf/api/base_classes/collection_resource.rb +613 -0
  5. data/lib/jamf/api/{abstract_classes → base_classes}/json_object.rb +110 -102
  6. data/lib/jamf/api/{abstract_classes → base_classes}/prestage.rb +56 -31
  7. data/lib/jamf/api/{abstract_classes → base_classes}/resource.rb +10 -6
  8. data/lib/jamf/api/{abstract_classes → base_classes}/singleton_resource.rb +4 -3
  9. data/lib/jamf/api/connection.rb +20 -12
  10. data/lib/jamf/api/connection/api_error.rb +8 -8
  11. data/lib/jamf/api/connection/token.rb +36 -15
  12. data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
  13. data/lib/jamf/api/json_objects/{location.rb → device_enrollment_device_sync_state.rb} +27 -41
  14. data/lib/jamf/api/json_objects/device_enrollment_sync_status.rb +1 -1
  15. data/lib/jamf/api/json_objects/{attachment.rb → locale.rb} +14 -23
  16. data/lib/jamf/api/json_objects/md_prestage_name.rb +1 -1
  17. data/lib/jamf/api/json_objects/md_prestage_names.rb +2 -2
  18. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
  19. data/lib/jamf/api/json_objects/prestage_assignment.rb +2 -2
  20. data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
  21. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
  22. data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
  23. data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
  24. data/lib/jamf/api/mixins/{abstract.rb → base_class.rb} +34 -16
  25. data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
  26. data/lib/jamf/api/mixins/change_log.rb +201 -51
  27. data/lib/jamf/api/{resources/collection_resources/extension_attribute.rb → mixins/filterable.rb} +20 -14
  28. data/lib/jamf/api/mixins/pageable.rb +208 -0
  29. data/lib/jamf/api/{json_objects/installed_application.rb → mixins/sortable.rb} +33 -33
  30. data/lib/jamf/api/resources/collection_resources/building.rb +16 -9
  31. data/lib/jamf/api/resources/collection_resources/category.rb +5 -4
  32. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +12 -5
  33. data/lib/jamf/api/resources/collection_resources/department.rb +1 -3
  34. data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +13 -13
  35. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
  36. data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +25 -23
  37. data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
  38. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
  39. data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
  40. data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
  41. data/lib/jamf/configuration.rb +7 -9
  42. data/lib/jamf/ruby_extensions.rb +1 -0
  43. data/lib/jamf/ruby_extensions/array.rb +1 -1
  44. data/lib/jamf/ruby_extensions/array/utils.rb +3 -3
  45. data/lib/jamf/{api/resources/collection_resources/computer.rb → ruby_extensions/dig.rb} +22 -19
  46. data/lib/jamf/validate.rb +63 -24
  47. data/lib/jamf/version.rb +1 -1
  48. data/lib/jss.rb +4 -1
  49. data/lib/jss/api_connection.rb +111 -433
  50. data/lib/jss/api_object.rb +16 -13
  51. data/lib/jss/api_object/advanced_search.rb +27 -26
  52. data/lib/jss/api_object/app_store_country_codes.rb +298 -0
  53. data/lib/jss/api_object/categorizable.rb +1 -1
  54. data/lib/jss/api_object/computer.rb +13 -0
  55. data/lib/jss/api_object/configuration_profile.rb +60 -4
  56. data/lib/jss/api_object/directory_binding.rb +273 -0
  57. data/lib/jss/api_object/directory_binding_type.rb +96 -0
  58. data/lib/jss/api_object/directory_binding_type/active_directory.rb +539 -0
  59. data/lib/jss/api_object/directory_binding_type/admitmac.rb +594 -0
  60. data/lib/jss/api_object/directory_binding_type/centrify.rb +226 -0
  61. data/lib/jss/api_object/directory_binding_type/open_directory.rb +178 -0
  62. data/lib/jss/api_object/directory_binding_type/powerbroker_identity_services.rb +73 -0
  63. data/lib/jss/api_object/disk_encryption_configurations.rb +114 -0
  64. data/lib/jss/api_object/distribution_point.rb +97 -37
  65. data/lib/jss/api_object/dock_item.rb +143 -0
  66. data/lib/jss/api_object/ebook.rb +1 -2
  67. data/lib/jss/api_object/extendable.rb +1 -1
  68. data/lib/jss/api_object/extension_attribute.rb +4 -3
  69. data/lib/jss/api_object/group.rb +33 -2
  70. data/lib/jss/api_object/mac_application.rb +107 -8
  71. data/lib/jss/api_object/mobile_device_application.rb +12 -0
  72. data/lib/jss/api_object/network_segment.rb +195 -70
  73. data/lib/jss/api_object/package.rb +105 -40
  74. data/lib/jss/api_object/patch_source.rb +10 -9
  75. data/lib/jss/api_object/policy.rb +596 -32
  76. data/lib/jss/api_object/printer.rb +446 -0
  77. data/lib/jss/api_object/scopable.rb +10 -15
  78. data/lib/jss/api_object/scopable/scope.rb +371 -55
  79. data/lib/jss/api_object/self_servable.rb +17 -9
  80. data/lib/jss/api_object/uploadable.rb +1 -1
  81. data/lib/jss/api_object/user.rb +42 -1
  82. data/lib/jss/api_object/vpp_account.rb +209 -0
  83. data/lib/jss/api_object/vppable.rb +169 -13
  84. data/lib/jss/composer.rb +1 -1
  85. data/lib/jss/exceptions.rb +3 -0
  86. data/lib/jss/server.rb +15 -0
  87. data/lib/jss/utility.rb +143 -52
  88. data/lib/jss/validate.rb +53 -10
  89. data/lib/jss/version.rb +1 -1
  90. metadata +56 -61
  91. data/lib/jamf/api/abstract_classes/advanced_search.rb +0 -86
  92. data/lib/jamf/api/abstract_classes/collection_resource.rb +0 -433
  93. data/lib/jamf/api/abstract_classes/generic_reference.rb +0 -145
  94. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +0 -126
  95. data/lib/jamf/api/json_objects/account_prefs.rb +0 -79
  96. data/lib/jamf/api/json_objects/android_details.rb +0 -139
  97. data/lib/jamf/api/json_objects/appletv_details.rb +0 -110
  98. data/lib/jamf/api/json_objects/cellular_network.rb +0 -151
  99. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +0 -67
  100. data/lib/jamf/api/json_objects/criterion.rb +0 -152
  101. data/lib/jamf/api/json_objects/extension_attribute_value.rb +0 -128
  102. data/lib/jamf/api/json_objects/installed_certificate.rb +0 -53
  103. data/lib/jamf/api/json_objects/installed_configuration_profile.rb +0 -67
  104. data/lib/jamf/api/json_objects/installed_ebook.rb +0 -58
  105. data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +0 -59
  106. data/lib/jamf/api/json_objects/ios_details.rb +0 -244
  107. data/lib/jamf/api/json_objects/mobile_device_details.rb +0 -219
  108. data/lib/jamf/api/json_objects/mobile_device_security.rb +0 -101
  109. data/lib/jamf/api/json_objects/purchasing_data.rb +0 -125
  110. data/lib/jamf/api/mixins/locatable.rb +0 -124
  111. data/lib/jamf/api/mixins/referable.rb +0 -92
  112. data/lib/jamf/api/resources/collection_resources/account.rb +0 -163
  113. data/lib/jamf/api/resources/collection_resources/advanced_mobile_device_search.rb +0 -52
  114. data/lib/jamf/api/resources/collection_resources/advanced_user_search.rb +0 -52
  115. data/lib/jamf/api/resources/collection_resources/mobile_device.rb +0 -315
  116. data/lib/jamf/api/resources/collection_resources/site.rb +0 -77
  117. data/lib/jamf/api/resources/singleton_resources/authorization.rb +0 -88
  118. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +0 -139
  119. data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +0 -95
@@ -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
- # IMPORTANT:
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 Mobiledevices?
234
+ # what type of target is this scope for? Computers or MobileDevices?
230
235
  attr_reader :target_class
231
236
 
232
- # @return [Hash<Array>]
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 @inclusions Hash is ignored, and all
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
- # @return [Hash<Array>]
250
+ # The items which form the base scope of included targets
256
251
  #
257
- # The items in these arrays are the limitations applied to targets in the @inclusions .
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
- # The arrays of names are:
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
- # - :users
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
- # @return [Hash<Array>]
277
+ # The items in these arrays are the exclusions applied to targets in the @targets .
267
278
  #
268
- # The items in these arrays are the exclusions applied to targets in the @inclusions .
269
- #
270
- # The arrays of names are:
271
- # - :targets
272
- # - :target_groups
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
- @inclusion_keys = [@target_key, @group_key] + INCLUSIONS
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
- @inclusions = {}
311
- @inclusion_keys.each do |k|
322
+ @targets = {}
323
+ @target_keys.each do |k|
312
324
  raw_scope[k] ||= []
313
- @inclusions[k] = raw_scope[k].compact.map { |n| n[:id].to_i }
314
- end # @inclusion_keys.each do |k|
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
- @inclusions = {}
386
- @inclusion_keys.each { |k| @inclusions[k] = [] }
401
+ @targets = {}
402
+ @target_keys.each { |k| @targets[k] = [] }
387
403
  @all_targets = true
388
404
  if clear
389
405
  @limitations = {}
@@ -428,9 +444,9 @@ module JSS
428
444
  item_id
429
445
  end # each
430
446
 
431
- return nil if list.sort == @inclusions[key].sort
447
+ return nil if list.sort == @targets[key].sort
432
448
 
433
- @inclusions[key] = list
449
+ @targets[key] = list
434
450
  @all_targets = false
435
451
  @container&.should_update
436
452
  end # sinclude_in_scope
@@ -458,11 +474,11 @@ 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 @inclusions[key]&.include?(item_id)
477
+ return if @targets[key]&.include?(item_id)
462
478
 
463
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
- @inclusions[key] << item_id
481
+ @targets[key] << item_id
466
482
  @all_targets = false
467
483
  @container&.should_update
468
484
  end
@@ -483,9 +499,9 @@ 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 @inclusions[key]&.include?(item_id)
502
+ return unless @targets[key]&.include?(item_id)
487
503
 
488
- @inclusions[key].delete item_id
504
+ @targets[key].delete item_id
489
505
  @container&.should_update
490
506
  end
491
507
  alias remove_inclusion remove_target
@@ -600,10 +616,12 @@ module JSS
600
616
  list.map! do |ident|
601
617
  item_id = validate_item(:exclusion, key, ident)
602
618
  case key
603
- when *@inclusion_keys
604
- raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included." if @inclusions[key]&.include?(item_id)
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
- if @limitations[key]&.include?(item_id)
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."
608
626
  end
609
627
  end
@@ -634,7 +652,7 @@ module JSS
634
652
  item_id = validate_item(:exclusion, key, item)
635
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 @inclusions[key]&.include?(item)
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
657
  raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]&.include?(item)
640
658
 
@@ -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
- @inclusions.each do |klass, list|
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
- @exclusions.each do |klass, list|
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
- # Aliases
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
- alias all_targets? all_targets
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 @inclusion_keys
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