ruby-jss 1.2.9 → 1.5.1

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.

Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +196 -1
  3. data/lib/jamf.rb +10 -3
  4. data/lib/jamf/api/abstract_classes/collection_resource.rb +329 -150
  5. data/lib/jamf/api/abstract_classes/generic_reference.rb +9 -1
  6. data/lib/jamf/api/abstract_classes/json_object.rb +107 -83
  7. data/lib/jamf/api/abstract_classes/prestage.rb +55 -30
  8. data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +21 -0
  9. data/lib/jamf/api/abstract_classes/resource.rb +4 -4
  10. data/lib/jamf/api/abstract_classes/singleton_resource.rb +1 -1
  11. data/lib/jamf/api/connection.rb +20 -12
  12. data/lib/jamf/api/connection/api_error.rb +8 -8
  13. data/lib/jamf/api/connection/token.rb +36 -15
  14. data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +14 -1
  15. data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
  16. data/lib/jamf/api/json_objects/device_enrollment_device_sync_state.rb +81 -0
  17. data/lib/jamf/api/json_objects/locale.rb +59 -0
  18. data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
  19. data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
  20. data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
  21. data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
  22. data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
  23. data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
  24. data/lib/jamf/api/mixins/change_log.rb +201 -51
  25. data/lib/jamf/api/mixins/filterable.rb +51 -0
  26. data/lib/jamf/api/mixins/pageable.rb +208 -0
  27. data/lib/jamf/api/mixins/sortable.rb +59 -0
  28. data/lib/jamf/api/resources/collection_resources/building.rb +19 -8
  29. data/lib/jamf/api/resources/collection_resources/category.rb +5 -3
  30. data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +11 -4
  31. data/lib/jamf/api/resources/collection_resources/department.rb +1 -1
  32. data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +13 -13
  33. data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
  34. data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +24 -22
  35. data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
  36. data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
  37. data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +14 -14
  38. data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
  39. data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
  40. data/lib/jamf/configuration.rb +7 -9
  41. data/lib/jamf/ruby_extensions.rb +1 -0
  42. data/lib/jamf/ruby_extensions/array.rb +1 -1
  43. data/lib/jamf/ruby_extensions/array/utils.rb +3 -3
  44. data/lib/jamf/ruby_extensions/dig.rb +52 -0
  45. data/lib/jamf/validate.rb +63 -24
  46. data/lib/jamf/version.rb +1 -1
  47. data/lib/jss.rb +4 -1
  48. data/lib/jss/api_connection.rb +110 -397
  49. data/lib/jss/api_object.rb +16 -13
  50. data/lib/jss/api_object/advanced_search.rb +27 -26
  51. data/lib/jss/api_object/app_store_country_codes.rb +298 -0
  52. data/lib/jss/api_object/categorizable.rb +1 -1
  53. data/lib/jss/api_object/computer.rb +5 -1
  54. data/lib/jss/api_object/configuration_profile.rb +34 -3
  55. data/lib/jss/api_object/directory_binding.rb +273 -0
  56. data/lib/jss/api_object/directory_binding_type.rb +96 -0
  57. data/lib/jss/api_object/directory_binding_type/active_directory.rb +539 -0
  58. data/lib/jss/api_object/directory_binding_type/admitmac.rb +594 -0
  59. data/lib/jss/api_object/directory_binding_type/centrify.rb +226 -0
  60. data/lib/jss/api_object/directory_binding_type/open_directory.rb +178 -0
  61. data/lib/jss/api_object/directory_binding_type/powerbroker_identity_services.rb +73 -0
  62. data/lib/jss/api_object/disk_encryption_configurations.rb +114 -0
  63. data/lib/jss/api_object/distribution_point.rb +97 -37
  64. data/lib/jss/api_object/dock_item.rb +143 -0
  65. data/lib/jss/api_object/ebook.rb +1 -2
  66. data/lib/jss/api_object/extendable.rb +68 -32
  67. data/lib/jss/api_object/extension_attribute.rb +4 -3
  68. data/lib/jss/api_object/group.rb +33 -2
  69. data/lib/jss/api_object/mac_application.rb +107 -8
  70. data/lib/jss/api_object/mobile_device.rb +3 -0
  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 +491 -7
  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 +386 -71
  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 +8 -22
  88. data/lib/jss/validate.rb +53 -10
  89. data/lib/jss/version.rb +1 -1
  90. metadata +50 -22
@@ -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.
@@ -226,56 +230,64 @@ module JSS
226
230
  # if we can't connect to LDAP servers for verification?
227
231
  attr_accessor :unable_to_verify_ldap_entries
228
232
 
229
- # what type of target is this scope for? Computers or Mobiledevices?
233
+ # what type of target is this scope for? Computers or MobileDevices?
230
234
  attr_reader :target_class
231
235
 
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
236
+ # what type of target group is this scope for? ComputerGroups or MobileDeviceGroups?
237
+ attr_reader :group_class
245
238
 
246
239
  # @return [Boolean]
247
240
  #
248
241
  # Does this scope cover all targets?
249
242
  #
250
- # If this is true, the @inclusions Hash is ignored, and all
243
+ # If this is true, the @targets Hash is ignored, and all
251
244
  # targets in the JSS form the base scope.
252
245
  #
253
246
  attr_reader :all_targets
247
+ alias all_targets? all_targets
248
+
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.
261
+ #
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 .
258
268
  #
259
- # The arrays of names are:
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>]
267
- #
268
- # The items in these arrays are the exclusions applied to targets in the @inclusions .
277
+ # The items in these arrays are the exclusions applied to targets in the @targets .
269
278
  #
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 = {}
@@ -392,7 +408,7 @@ module JSS
392
408
  @exclusions = {}
393
409
  @exclusion_keys.each { |k| @exclusions[k] = [] }
394
410
  end
395
- @container&.should_update
411
+ @container.should_update if @container
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]&.include?(item_id)
439
+ if @exclusions[key] && @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 == @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
- @container&.should_update
451
+ @container.should_update if @container
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 @inclusions[key]&.include?(item_id)
477
+ return if @targets[key] && @exclusions[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]&.include?(item_id)
479
+ raise JSS::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key] && @exclusions[key].include?(item_id)
464
480
 
465
- @inclusions[key] << item_id
481
+ @targets[key] << item_id
466
482
  @all_targets = false
467
- @container&.should_update
483
+ @container.should_update if @container
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 @inclusions[key]&.include?(item_id)
502
+ return unless @targets[key] && @exclusions[key].include?(item_id)
487
503
 
488
- @inclusions[key].delete item_id
489
- @container&.should_update
504
+ @targets[key].delete item_id
505
+ @container.should_update if @container
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]&.include?(item_id)
532
+ if @exclusions[key] && @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&.should_update
542
+ @container.should_update if @container
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]&.include?(item_id)
564
+ return nil if @limitations[key] && @exclusions[key].include?(item_id)
549
565
 
550
- if @exclusions[key]&.include?(item_id)
566
+ if @exclusions[key] && @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&.should_update
571
+ @container.should_update if @container
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]&.include?(item_id)
591
+ return unless @limitations[key] && @exclusions[key].include?(item_id)
576
592
 
577
593
  @limitations[key].delete item_id
578
- @container&.should_update
594
+ @container.should_update if @container
579
595
  end ###
580
596
 
581
597
  # Replace an exclusion list for this scope
@@ -600,10 +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 *@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
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already explicitly included." if @targets[key] && @exclusions[key].include?(item_id)
605
621
  when *LIMITATIONS
606
- if @limitations[key]&.include?(item_id)
622
+ if @limitations[key] && @exclusions[key].include?(item_id)
607
623
  raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{ident}' because it's already an explicit limitation."
608
624
  end
609
625
  end
@@ -613,7 +629,7 @@ module JSS
613
629
  return nil if list.sort == @exclusions[key].sort
614
630
 
615
631
  @exclusions[key] = list
616
- @container&.should_update
632
+ @container.should_update if @container
617
633
  end # limit scope
618
634
 
619
635
  # Add a single item for exclusions of this scope.
@@ -632,14 +648,14 @@ module JSS
632
648
  def add_exclusion(key, item)
633
649
  key = pluralize_key(key)
634
650
  item_id = validate_item(:exclusion, key, item)
635
- return if @exclusions[key]&.include?(item_id)
651
+ return if @exclusions[key] && @exclusions[key].include?(item_id)
636
652
 
637
- raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @inclusions[key]&.include?(item)
653
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} scope to '#{item}' because it's already explicitly included." if @targets[key] && @targets[key].include?(item)
638
654
 
639
- raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key]&.include?(item)
655
+ raise JSS::AlreadyExistsError, "Can't exclude #{key} '#{item}' because it's already an explicit limitation." if @limitations[key] && @limitations[key].include?(item)
640
656
 
641
657
  @exclusions[key] << item_id
642
- @container&.should_update
658
+ @container.should_update if @container
643
659
  end
644
660
 
645
661
  # Remove a single item for exclusions of this scope
@@ -656,10 +672,10 @@ module JSS
656
672
  def remove_exclusion(key, item)
657
673
  key = pluralize_key(key)
658
674
  item_id = validate_item :exclusion, key, item, error_if_not_found: false
659
- return unless @exclusions[key]&.include?(item_id)
675
+ return unless @exclusions[key] && @exclusions[key].include?(item_id)
660
676
 
661
677
  @exclusions[key].delete item_id
662
- @container&.should_update
678
+ @container.should_update if @container
663
679
  end
664
680
 
665
681
  # @api private
@@ -672,7 +688,8 @@ module JSS
672
688
  scope = REXML::Element.new 'scope'
673
689
  scope.add_element(@all_key.to_s).text = @all_targets
674
690
 
675
- @inclusions.each do |klass, list|
691
+ @target_keys.each do |klass|
692
+ list = @targets[klass]
676
693
  list.compact!
677
694
  list.delete 0
678
695
  list_as_hashes = list.map { |i| { id: i } }
@@ -702,7 +719,8 @@ module JSS
702
719
  end
703
720
 
704
721
  exclusions = scope.add_element('exclusions')
705
- @exclusions.each do |klass, list|
722
+ @exclusion_keys.each do |klass|
723
+ list = @exclusions[klass]
706
724
  list.compact!
707
725
  list.delete 0
708
726
  if klass == :jamf_ldap_users
@@ -737,9 +755,60 @@ module JSS
737
755
  vars
738
756
  end
739
757
 
740
- # Aliases
758
+ # Return a hash of id => name for all machines in the target class
759
+ # that are within this scope.
760
+ #
761
+ # WARNING: This must instantiate all machines in the target class.
762
+ # It will still be slow, at least the first time for each target class.
763
+ # On the upside, the instantiated machines will be cached, so generating
764
+ # this list for other scopes with the same target class will be much
765
+ # much faster.
766
+ # In tests, 1600 Computers took about 7 minutes the first time,
767
+ # but less than 1 second after caching.
768
+ #
769
+ # See also the warning for #in_scope?
770
+ #
771
+ # @return [Hash{Integer => String}]
772
+ #
773
+ ################
774
+ def scoped_machines
775
+ scoped_machines = {}
776
+ @target_class.all_objects.each do |machine|
777
+ scoped_machines[machine.id] = machine.name if in_scope? machine
778
+ end
779
+ scoped_machines
780
+ end
781
+
782
+ # is a given machine is in this scope?
783
+ #
784
+ # For a parameter you may pass either an instantiated
785
+ # JSS::MobileDevice or JSS::Computer, or an identifier for one.
786
+ # If an identifier is passed, it is not instantiated, but an API
787
+ # request is made for just the required subsets of data, thus
788
+ # speeding things up a bit when calling this method many times.
789
+ #
790
+ # WARNING: For scopes that include Jamf Users and Jamf User Groups
791
+ # as targets or exclusions, this method may return an incorrect value.
792
+ # See the discussion in the documentation for the Scopable::Scope class
793
+ # under 'IMPORTANT - Users & User Groups in Targets and Exclusions'
794
+ #
795
+ # NOTE: currently in-range iBeacons are transient, and are not reported
796
+ # to the JSS as inventory data. As such they are ignored in this result.
797
+ # If a scope contains iBeacon limitations or exclusions, it is up to
798
+ # the user to be aware of that when evaluating the meaning of this result.
799
+ #
800
+ # @param machine[Integer, String, JSS::MobileDevice, JSS::Computer]
801
+ # Either an identifier for the machine, or an instantiated object
802
+ #
803
+ # @return [Boolean]
804
+ #
805
+ ##############
806
+ def in_scope?(machine)
807
+ machine_data = fetch_machine_data machine
808
+
809
+ a_target?(machine_data) && within_limitations?(machine_data) && !excluded?(machine_data)
810
+ end
741
811
 
742
- alias all_targets? all_targets
743
812
 
744
813
  # Private Instance Methods
745
814
  #####################################
@@ -765,7 +834,7 @@ module JSS
765
834
  # which keys allowed depends on how the item is used...
766
835
  possible_keys =
767
836
  case realm
768
- when :target then @inclusion_keys
837
+ when :target then @target_keys
769
838
  when :limitation then LIMITATIONS
770
839
  when :exclusion then @exclusion_keys
771
840
  else
@@ -809,6 +878,252 @@ module JSS
809
878
  end
810
879
  end
811
880
 
881
+ # The data used by the methods that figure out if a machine is
882
+ # in this scope, a Hash of Hashes. the sub hashes are:
883
+ #
884
+ # general: the 'general' subset
885
+ # location: the 'location' subset
886
+ # group_ids: an Array of the group ids to which the machine belongs.
887
+ #
888
+ # @param machine[Integer, String, JSS::MobileDevice, JSS::Computer]
889
+ # Either an identifier for the machine, or an instantiated object
890
+ #
891
+ # @return
892
+ #
893
+ def fetch_machine_data(machine)
894
+ case machine
895
+ when JSS::Computer
896
+ raise JSS::InvalidDataError, "Targets of this scope must be #{@target_class}" unless @target_class == JSS::Computer
897
+
898
+ general = machine.init_data[:general]
899
+ location = machine.init_data[:location]
900
+ group_ids = group_ids machine.computer_groups
901
+
902
+ # put in standardize place for easier use
903
+ # MDevs already have this at general[:managed]
904
+ general[:managed] = general[:remote_management][:managed]
905
+
906
+ when JSS::MobileDevice
907
+ raise JSS::InvalidDataError, "Targets of this scope must be #{@target_class}" unless @target_class == JSS::MobileDevice
908
+
909
+ general = machine.init_data[:general]
910
+ location = machine.init_data[:location]
911
+ group_ids = group_ids machine.mobile_device_groups
912
+
913
+ else
914
+ general, location, group_ids = fetch_subsets(machine)
915
+ end # case
916
+
917
+ {
918
+ general: general,
919
+ location: location,
920
+ group_ids: group_ids
921
+ }
922
+ end
923
+
924
+ # When we are given an indentifier for a machine,
925
+ # fetch just the subsets of API data we need to
926
+ # determine if the machine is in this scope
927
+ #
928
+ # @param ident[String, Integer]
929
+ #
930
+ # @return [Array] the general, locacation, and parsed group IDs
931
+ #
932
+ def fetch_subsets(ident)
933
+ id = @target_class.valid_id ident
934
+ raise JSS::NoSuchItemError, "No #{@target_class} matching #{machine}" unless id
935
+
936
+ if @target_class == JSS::MobileDevice
937
+ grp_subset = 'MobileDeviceGroups'
938
+ top_key = :mobile_device
939
+ else
940
+ grp_subset = 'GroupsAccounts'
941
+ top_key = :computer
942
+ end
943
+ subset_rsrc = "#{@target_class::RSRC_BASE}/id/#{id}/subset/General&Location&#{grp_subset}"
944
+ data = container.api.get_rsrc(subset_rsrc)[top_key]
945
+ grp_data =
946
+ if @target_class == JSS::MobileDevice
947
+ data[:mobile_device_groups]
948
+ else
949
+ data[:groups_accounts][:computer_group_memberships]
950
+ end
951
+
952
+ [data[:general], data[:location], group_ids(grp_data)]
953
+ end
954
+
955
+ # Given the raw API data for a machines group membership,
956
+ # return an array of the IDs of the groups.
957
+ #
958
+ # @param raw[Array] The API array of the machine's group memberships
959
+ #
960
+ # @return [Array] The ID's of the groups to which the machine belongs.
961
+ #
962
+ def group_ids(raw)
963
+ if @target_class == JSS::MobileDevice
964
+ raw.map { |mdg| mdg[:id] }
965
+ else
966
+ names_to_ids = @group_class.map_all_ids_to(:name).invert
967
+ raw.map { |gn| names_to_ids[gn] }
968
+ end
969
+ end
970
+
971
+ # @param machine_data[Hash] See #fetch_machine_data
972
+ # @return [Boolean]
973
+ ################
974
+ def a_target?(machine_data)
975
+ return false unless machine_data[:general][:managed]
976
+ return true if \
977
+ all_targets? || \
978
+ machine_directly_scoped?(machine_data, :target) || \
979
+ machine_in_scope_group?(machine_data, :target) || \
980
+ machine_in_scope_buildings?(machine_data, :target) || \
981
+ machine_in_scope_depts?(machine_data, :target)
982
+
983
+ false
984
+ end
985
+
986
+ # @param machine_data[Hash] See #fetch_machine_data
987
+ # @return [Boolean]
988
+ ################
989
+ def within_limitations?(machine_data)
990
+ return false if \
991
+ machine_in_scope_netsegs?(machine_data, :limitation) == false || \
992
+ machine_in_scope_jamf_ldap_users_list?(machine_data, :limitation) == false || \
993
+ machine_in_scope_ldap_usergroup_list?(machine_data, :limitation) == false
994
+
995
+ true
996
+ end
997
+
998
+ # @param machine_data[Hash] See #fetch_machine_data
999
+ # @return [Boolean]
1000
+ ################
1001
+ def excluded?(machine_data)
1002
+ return true if
1003
+ machine_directly_scoped?(machine_data, :exclusion) || \
1004
+ machine_in_scope_group?(machine_data, :exclusion) || \
1005
+ machine_in_scope_buildings?(machine_data, :exclusion) || \
1006
+ machine_in_scope_depts?(machine_data, :exclusion) || \
1007
+ machine_in_scope_netsegs?(machine_data, :exclusion) || \
1008
+ machine_in_scope_jamf_ldap_users_list?(machine_data, :exclusion) || \
1009
+ machine_in_scope_ldap_usergroup_list?(machine_data, :exclusion)
1010
+
1011
+ false
1012
+ end
1013
+
1014
+ # @param machine_data[Hash] See #fetch_machine_data
1015
+ # @param part[Symbol] either :target or :exclusion
1016
+ # @return [Boolean] Is the machine directly spcified in this part of the scope?
1017
+ ################
1018
+ def machine_directly_scoped?(machine_data, part)
1019
+ scope_list = part == :target ? @targets[:direct_targets] : @exclusions[:direct_exclusions]
1020
+ scope_list.include? machine_data[:general][:id]
1021
+ end
1022
+
1023
+ # @param machine_data[Hash] See #fetch_machine_data
1024
+ # @param part[Symbol] either :target or :exclusion
1025
+ # @return [Boolean] Is the machine a member of any group listed in this part of the scope?
1026
+ ################
1027
+ def machine_in_scope_group?(machine_data, part)
1028
+ scope_list = part == :target ? @targets[:group_targets] : @exclusions[:group_exclusions]
1029
+ # if the list is empty, return nil
1030
+ return if scope_list.empty?
1031
+
1032
+ # if the intersection of the machine's group ids, and those of the scope part
1033
+ # is not empty, then the machine is in at least one of the groups
1034
+ !(machine_data[:group_ids] & scope_list).empty?
1035
+ end
1036
+
1037
+ # @param machine_data[Hash] See #fetch_machine_data
1038
+ # @param part[Symbol] either :target or :exclusion
1039
+ # @return [Boolean] Is the machine in any building listed in this part of the scope?
1040
+ #################
1041
+ def machine_in_scope_buildings?(machine_data, part)
1042
+ scope_list = part == :target ? @targets[:buildings] : @exclusions[:buildings]
1043
+
1044
+ # nil if empty
1045
+ return if scope_list.empty?
1046
+ # false if no building for the machine - it isn't in any dept
1047
+ return false if machine_data[:location][:building].to_s.empty?
1048
+
1049
+ building_id = JSS::Building.map_all_ids_to(:name).invert[machine_data[:location][:building]]
1050
+ scope_list.include? building_id
1051
+ end
1052
+
1053
+ # @param machine_data[Hash] See #fetch_machine_data
1054
+ # @param part[Symbol] either :target or :exclusion
1055
+ # @return [Boolean] Is the machine in any department listed in this part of the scope?
1056
+ #################
1057
+ def machine_in_scope_depts?(machine_data, part)
1058
+ scope_list = part == :target ? @targets[:departments] : @exclusions[:departments]
1059
+
1060
+ # nil if empty
1061
+ return if scope_list.empty?
1062
+ # false if no dept for the machine - it isn't in any dept
1063
+ return false if machine_data[:location][:department].to_s.empty?
1064
+
1065
+ dept_id = JSS::Department.map_all_ids_to(:name).invert[machine_data[:location][:department]]
1066
+
1067
+ scope_list.include? dept_id
1068
+ end
1069
+
1070
+ # @param machine_data[Hash] See #fetch_machine_data
1071
+ # @param part[Symbol] either :limitation or :exclusion
1072
+ # @return [Boolean] Is the machine in any NetworkSegment listed in this part of the scope?
1073
+ ##################
1074
+ def machine_in_scope_netsegs?(machine_data, part)
1075
+ scope_list = part == :limitation ? @limitations[:network_segments] : @exclusions[:network_segments]
1076
+
1077
+ # nil if no netsegs in scope part
1078
+ return if scope_list.empty?
1079
+
1080
+ ip = @target_class == JSS::Computer ? machine_data[:general][:last_reported_ip] : machine_data[:general][:ip_address]
1081
+ # false if no ip for machine - it isn't in a any of the segs
1082
+ return false if ip.to_s.empty?
1083
+
1084
+ mach_segs = JSS::NetworkSegment.network_segments_for_ip ip
1085
+
1086
+ # if the intersection is not empty, then the machine is in at least one of the net segs
1087
+ !(mach_segs & scope_list).empty?
1088
+ end
1089
+
1090
+ # @param machine_data[Hash] See #fetch_machine_data
1091
+ # @param part[Symbol] either :limitation or :exclusion
1092
+ # @return [Boolean] Is the user of this machine in the list of jamf/ldap users in this part of the scope?
1093
+ ##################
1094
+ def machine_in_scope_jamf_ldap_users_list?(machine_data, part)
1095
+ scope_list = part == :limitation ? @limitations[:jamf_ldap_users] : @exclusions[:jamf_ldap_users]
1096
+
1097
+ # nil if the list is empty
1098
+ return if scope_list.empty?
1099
+
1100
+ scope_list.include? machine_data[:location][:username]
1101
+ end
1102
+
1103
+ # @param machine_data[Hash] See #fetch_machine_data
1104
+ # @param part[Symbol] either :limitation or :exclusion
1105
+ # @return [Boolean] Is the user of this machine a member of any of the LDAP groups in in this part of the scope?
1106
+ ##################
1107
+ def machine_in_scope_ldap_usergroup_list?(machine_data, part)
1108
+ scope_list = part == :limitation ? @limitations[:ldap_user_groups] : @exclusions[:ldap_user_groups]
1109
+
1110
+ # nil if the list is empty
1111
+ return if scope_list.empty?
1112
+
1113
+ # loop thru them checking to see if the user is a member
1114
+ scope_list.each do |ldapgroup|
1115
+ server = JSS::LDAPServer.server_for_group ldapgroup
1116
+ # if the group doesn't exist in any LDAP the user isn't a part of it
1117
+ next unless server
1118
+
1119
+ # if the user name is in any group, return true
1120
+ return true if JSS::LDAPServer.check_membership server, machine_data[:location][:username], ldapgroup
1121
+ end
1122
+
1123
+ # if we're here, not in any group
1124
+ false
1125
+ end
1126
+
812
1127
  end # class Scope
813
1128
 
814
1129
  end # module Scopable