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
@@ -121,9 +121,31 @@ module JSS
121
121
  # @return [Array<String>] The current file names
122
122
  #
123
123
  def self.all_filenames(api: JSS.api)
124
- pkgs_in_use = []
125
- all_ids.each { |pkg_id| pkgs_in_use << fetch(id: pkg_id, api: api).filename }
126
- pkgs_in_use.compact
124
+ all_filenames_by(:id, api: api).values
125
+ end
126
+
127
+ # A Hash of all dist-point filenames used by all JSS packages, keyed by
128
+ # package name or id
129
+ #
130
+ # Slow cuz we have to instantiate every pkg
131
+ #
132
+ # @param key[Symbol] either :id, or :name
133
+ #
134
+ # @param api[JSS::APIConnection] an API connection to use
135
+ # Defaults to the corrently active API. See {JSS::APIConnection}
136
+ #
137
+ # @return [Hash{Ingeter,String => String}] The current file names by key
138
+ #
139
+ def self.all_filenames_by(key, api: JSS.api)
140
+ raise ArgumentError, 'key must be :id or :name' unless %i[id name].include? key
141
+
142
+ files_in_use = {}
143
+ all_ids(:refresh).each do |pkg_id|
144
+ pkg = fetch id: pkg_id, api: api
145
+ files_in_use[pkg.send(key)] = pkg.filename
146
+ end
147
+
148
+ files_in_use
127
149
  end
128
150
 
129
151
  # An array of String filenames for all files DIST_POINT_PKGS_FOLDER
@@ -140,14 +162,17 @@ module JSS
140
162
  # @param api[JSS::APIConnection] an API connection to use
141
163
  # Defaults to the corrently active API. See {JSS::APIConnection}
142
164
  #
165
+ # @param dist_point [String,Integer] the name or id of the distribution
166
+ # point to use. Defaults to the Master Dist. Point
167
+ #
143
168
  # @return [Array<String>] The orphaned files
144
169
  #
145
- def self.orphaned_files(ro_pw, unmount = true, api: JSS.api)
146
- mdp = JSS::DistributionPoint.master_distribution_point api: api
147
- pkgs_dir = mdp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
148
- files_on_mdp = pkgs_dir.children.map { |f| f.basename.to_s }
149
- mdp.unmount if unmount
150
- files_on_mdp - all_filenames(api: api)
170
+ def self.orphaned_files(ro_pw, unmount = true, api: JSS.api, dist_point: nil)
171
+ dp = fetch_dist_point(dist_point, api: api)
172
+ pkgs_dir = dp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
173
+ files_on_dp = pkgs_dir.children.map { |f| f.basename.to_s }
174
+ dp.unmount if unmount
175
+ files_on_dp - all_filenames(api: api)
151
176
  end
152
177
 
153
178
  # An array of String filenames for all filenames in any
@@ -164,14 +189,18 @@ module JSS
164
189
  # @param api[JSS::APIConnection] an API connection to use
165
190
  # Defaults to the corrently active API. See {JSS::APIConnection}
166
191
  #
192
+ # @param dist_point [String,Integer] the name or id of the distribution
193
+ # point to use. Defaults to the Master Dist. Point
194
+ #
195
+ #
167
196
  # @return [Array<String>] The orphaned files
168
197
  #
169
- def self.missing_files(ro_pw, unmount = true, api: JSS.api)
170
- mdp = JSS::DistributionPoint.master_distribution_point api: api
171
- pkgs_dir = mdp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
172
- files_on_mdp = pkgs_dir.children.map { |f| f.basename.to_s }
173
- mdp.unmount if unmount
174
- all_filenames(api: api) - files_on_mdp
198
+ def self.missing_files(ro_pw, unmount = true, api: JSS.api, dist_point: nil)
199
+ dp = fetch_dist_point(dist_point, api: api)
200
+ pkgs_dir = dp.mount(ro_pw, :ro) + DIST_POINT_PKGS_FOLDER
201
+ files_on_dp = pkgs_dir.children.map { |f| f.basename.to_s }
202
+ dp.unmount if unmount
203
+ all_filenames(api: api) - files_on_dp
175
204
  end
176
205
 
177
206
  # Given a file path, and hash type, generate the checksum for an arbitrary
@@ -189,6 +218,18 @@ module JSS
189
218
  CHECKSUM_HASH_TYPES[type].file(filepath).hexdigest
190
219
  end
191
220
 
221
+ # @param dist_point [String,Integer] the name or id of the distribution
222
+ # point to use. Defaults to the Master Dist. Point
223
+ #
224
+ # @return [JSS::DistributionPoint]
225
+ def self.fetch_dist_point(dist_point, api: JSS.api)
226
+ if dist_point
227
+ JSS::DistributionPoint.fetch dist_point, api: api
228
+ else
229
+ JSS::DistributionPoint.master_distribution_point api: api
230
+ end
231
+ end
232
+
192
233
  # Attributes
193
234
  #####################################
194
235
 
@@ -324,7 +365,7 @@ module JSS
324
365
  new_val = nil if new_val == ''
325
366
  new_val ||= @name
326
367
  return nil if new_val == @filename
327
- warn 'WARNING: you must change the filename on the master Distribution Point. See JSS::Package.update_master_filename.' if @in_jss
368
+
328
369
  @filename = new_val
329
370
  @need_to_update = true
330
371
  end
@@ -590,13 +631,17 @@ module JSS
590
631
  # @param chksum [String] the constants CHECKSUM_HASH_TYPE_SHA512 or
591
632
  # CHECKSUM_HASH_TYPE_MD5. Anything else means don't calc.
592
633
  #
634
+ # @param dist_point [String,Integer] the name or id of the distribution
635
+ # point to use. Defaults to the Master Dist. Point
636
+ #
593
637
  # @return [void]
594
638
  #
595
- def upload_master_file(local_file_path, rw_pw, unmount = true, chksum: DEFAULT_CHECKSUM_HASH_TYPE )
639
+ def upload_master_file(local_file_path, rw_pw, unmount = true, chksum: DEFAULT_CHECKSUM_HASH_TYPE, dist_point: nil)
596
640
  raise JSS::NoSuchItemError, 'Please create this package in the JSS before uploading it.' unless @in_jss
597
641
 
598
- mdp = JSS::DistributionPoint.master_distribution_point api: @api
599
- destination = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
642
+ dp = self.class.fetch_dist_point(dist_point, api: @api)
643
+
644
+ destination = dp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
600
645
 
601
646
  local_path = Pathname.new local_file_path
602
647
  raise JSS::NoSuchItemError, "Local file '#{@local_file}' doesn't exist" unless local_path.exist?
@@ -637,11 +682,11 @@ module JSS
637
682
 
638
683
  if CHECKSUM_HASH_TYPES.keys.include? chksum
639
684
  @checksum_type = chksum
640
- @checksum = calculate_checksum local_file: local_path, type: chksum, unmount: false
685
+ @checksum = calculate_checksum local_file: local_path, type: chksum, unmount: false, dist_point: dist_point
641
686
  @need_to_update = true
642
687
  end
643
688
  update if @need_to_update
644
- mdp.unmount if unmount
689
+ dp.unmount if unmount
645
690
  end # upload master file
646
691
 
647
692
  # Using either a local file, or the file on the master dist. point,
@@ -658,7 +703,7 @@ module JSS
658
703
  #
659
704
  # @return [void]
660
705
  #
661
- def reset_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true)
706
+ def reset_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
662
707
  type ||= DEFAULT_CHECKSUM_HASH_TYPE
663
708
 
664
709
  new_checksum = calculate_checksum(
@@ -666,7 +711,8 @@ module JSS
666
711
  local_file: local_file,
667
712
  rw_pw: rw_pw,
668
713
  ro_pw: ro_pw,
669
- unmount: unmount
714
+ unmount: unmount,
715
+ dist_point: dist_point
670
716
  )
671
717
  return if @checksum == new_checksum
672
718
 
@@ -694,11 +740,14 @@ module JSS
694
740
  # @param unmount [Boolean] Unmount the master dist point after using it.
695
741
  # Only used if the dist point is mounted. default: true
696
742
  #
743
+ # @param dist_point [String,Integer] the name or id of the distribution
744
+ # point to use. Defaults to the Master Dist. Point
745
+ #
697
746
  # @return [String] The calculated checksum
698
747
  #
699
- def calculate_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true )
748
+ def calculate_checksum(type: nil, local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
700
749
  type ||= DEFAULT_CHECKSUM_HASH_TYPE
701
- mdp = JSS::DistributionPoint.master_distribution_point api: @api
750
+ dp = self.class.fetch_dist_point(dist_point, api: @api)
702
751
 
703
752
  if local_file
704
753
  file_to_calc = local_file
@@ -712,10 +761,10 @@ module JSS
712
761
  else
713
762
  raise ArgumentError, 'Either rw_pw: or ro_pw: must be provided'
714
763
  end
715
- file_to_calc = mdp.mount(dppw, mnt) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
764
+ file_to_calc = dp.mount(dppw, mnt) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
716
765
  end
717
766
  new_checksum = self.class.calculate_checksum(file_to_calc, type)
718
- mdp.unmount if unmount && mdp.mounted?
767
+ dp.unmount if unmount && dp.mounted?
719
768
  new_checksum
720
769
  end
721
770
 
@@ -734,17 +783,21 @@ module JSS
734
783
  # @param unmount [Boolean] Unmount the master dist point after using it.
735
784
  # Only used if the dist point is mounted. default: true
736
785
  #
786
+ # @param dist_point [String,Integer] the name or id of the distribution
787
+ # point to use. Defaults to the Master Dist. Point
788
+ #
737
789
  # @return [Boolean] false if there is no checksum for this pkg, otherwise,
738
790
  # does the calculated checksum match the one stored for the pkg?
739
791
  #
740
- def checksum_valid?(local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true)
792
+ def checksum_valid?(local_file: nil, rw_pw: nil, ro_pw: nil, unmount: true, dist_point: nil )
741
793
  return false unless @checksum
742
794
  new_checksum = calculate_checksum(
743
795
  type: @checksum_type,
744
796
  local_file: local_file,
745
797
  rw_pw: rw_pw,
746
798
  ro_pw: ro_pw,
747
- unmount: unmount
799
+ unmount: unmount,
800
+ dist_point: dist_point
748
801
  )
749
802
  new_checksum == @checksum
750
803
  end
@@ -760,12 +813,16 @@ module JSS
760
813
  # @param rw_pw[String,Symbol] the password for the read/write account on the master Distribution Point,
761
814
  # or :prompt, or :stdin# where # is the line of stdin containing the password See {JSS::DistributionPoint#mount}
762
815
  #
816
+ # @param dist_point [String,Integer] the name or id of the distribution
817
+ # point to use. Defaults to the Master Dist. Point
818
+ #
763
819
  # @return [nil]
764
820
  #
765
- def update_master_filename(old_file_name, new_file_name, rw_pw, unmount = true)
821
+ def update_master_filename(old_file_name, new_file_name, rw_pw, unmount = true, dist_point: nil)
766
822
  raise JSS::NoSuchItemError, "#{old_file_name} does not exist in the jss." unless @in_jss
767
- mdp = JSS::DistributionPoint.master_distribution_point api: @api
768
- pkgs_dir = mdp.mount(rw_pw, :rw) + DIST_POINT_PKGS_FOLDER.to_s
823
+ dp = self.class.fetch_dist_point(dist_point, api: @api)
824
+
825
+ pkgs_dir = dp.mount(rw_pw, :rw) + DIST_POINT_PKGS_FOLDER.to_s
769
826
  old_file = pkgs_dir + old_file_name
770
827
  raise JSS::NoSuchItemError, "File not found on the master distribution point at #{DIST_POINT_PKGS_FOLDER}/#{old_file_name}." unless \
771
828
  old_file.exist?
@@ -775,7 +832,7 @@ module JSS
775
832
  new_file = pkgs_dir + (new_file_name + old_file.extname) if new_file.extname.empty?
776
833
 
777
834
  old_file.rename new_file
778
- mdp.unmount if unmount
835
+ dp.unmount if unmount
779
836
  nil
780
837
  end # update_master_filename
781
838
 
@@ -791,18 +848,21 @@ module JSS
791
848
  #
792
849
  # @param unmount[Boolean] whether or not ot unount the distribution point when finished.
793
850
  #
851
+ # @param dist_point [String,Integer] the name or id of the distribution
852
+ # point to use. Defaults to the Master Dist. Point
853
+ #
794
854
  # @return [Boolean] was the file deleted?
795
855
  #
796
- def delete_master_file(rw_pw, unmount = true)
797
- mdp = JSS::DistributionPoint.master_distribution_point api: @api
798
- file = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
856
+ def delete_master_file(rw_pw, unmount = true, dist_point: nil)
857
+ dp = self.class.fetch_dist_point(dist_point, api: @api)
858
+ file = dp.mount(rw_pw, :rw) + "#{DIST_POINT_PKGS_FOLDER}/#{@filename}"
799
859
  if file.exist?
800
860
  file.delete
801
861
  did_it = true
802
862
  else
803
863
  did_it = false
804
864
  end # if exists
805
- mdp.unmount if unmount
865
+ dp.unmount if unmount
806
866
  did_it
807
867
  end # delete master file
808
868
 
@@ -816,9 +876,13 @@ module JSS
816
876
  #
817
877
  # @param unmount[Boolean] whether or not ot unount the distribution point when finished.
818
878
  #
819
- def delete(delete_file: false, rw_pw: nil, unmount: true)
879
+ # @param dist_point [String,Integer] the name or id of the distribution
880
+ # point to use. Defaults to the Master Dist. Point
881
+ #
882
+ # @return [void]
883
+ def delete(delete_file: false, rw_pw: nil, unmount: true, dist_point: nil)
820
884
  super()
821
- delete_master_file(rw_pw, unmount) if delete_file
885
+ delete_master_file(rw_pw, unmount, dist_point: dist_point) if delete_file
822
886
  end
823
887
 
824
888
  # Install this package via the jamf binary 'install' command from the
@@ -885,7 +949,7 @@ module JSS
885
949
 
886
950
  # we'll re-add the filename below if needed.
887
951
  src_path = args[:alt_download_url].chomp "/#{@filename}"
888
-
952
+ using_http = true
889
953
  # use our appropriate dist. point for download
890
954
  else
891
955
  mdp = JSS::DistributionPoint.my_distribution_point api: @api
@@ -1021,6 +1085,7 @@ module JSS
1021
1085
 
1022
1086
  private
1023
1087
 
1088
+
1024
1089
  # Return the REST XML for this pkg, with the current values,
1025
1090
  # for saving or updating
1026
1091
  #
@@ -120,30 +120,31 @@ module JSS
120
120
 
121
121
  # Fetch either an internal or external patch source
122
122
  #
123
- # BUG: there's an API bug: fetching a non-existent ids
123
+ # BUG: there's an API bug: fetching a non-existent
124
124
  # which is why we rescue internal server errors.
125
125
  #
126
126
  # @see APIObject.fetch
127
127
  #
128
- def self.fetch(arg, api: JSS.api)
128
+ def self.fetch(searchterm = nil, **args)
129
129
  if self == JSS::PatchSource
130
130
  begin
131
- fetched = JSS::PatchInternalSource.fetch arg, api: api
132
- rescue RestClient::ResourceNotFound, RestClient::InternalServerError, JSS::NoSuchItemError
131
+ fetched = JSS::PatchInternalSource.fetch searchterm, **args
132
+ rescue
133
133
  fetched = nil
134
134
  end
135
135
  unless fetched
136
136
  begin
137
- fetched = JSS::PatchExternalSource.fetch arg, api: api
138
- rescue RestClient::ResourceNotFound, RestClient::InternalServerError, JSS::NoSuchItemError
137
+ fetched = JSS::PatchExternalSource.fetch searchterm, **args
138
+ rescue
139
139
  raise JSS::NoSuchItemError, 'No matching PatchSource found'
140
140
  end
141
141
  end
142
142
  return fetched
143
143
  end # if self == JSS::PatchSource
144
+
144
145
  begin
145
- super
146
- rescue RestClient::ResourceNotFound, RestClient::InternalServerError, JSS::NoSuchItemError
146
+ super searchterm, **args
147
+ rescue JSS::NoSuchItemError
147
148
  raise JSS::NoSuchItemError, "No matching #{self::RSRC_OBJECT_KEY} found"
148
149
  end
149
150
  end
@@ -211,7 +212,7 @@ module JSS
211
212
  begin
212
213
  # TODO: remove this and adjust parsing when jamf fixes the JSON
213
214
  raw = JSS::XMLWorkaround.data_via_xml(rsrc, AVAILABLE_TITLES_DATA_MAP, api)
214
- rescue RestClient::ResourceNotFound
215
+ rescue JSS::NoSuchItemError
215
216
  return []
216
217
  end
217
218
 
@@ -148,6 +148,12 @@ module JSS
148
148
  monthly: 'Once every month'
149
149
  }.freeze
150
150
 
151
+ RETRY_EVENTS = {
152
+ none: 'none',
153
+ checkin: 'check-in',
154
+ trigger: 'trigger'
155
+ }.freeze
156
+
151
157
  RESTART_WHEN = {
152
158
  if_pkg_requires: 'Restart if a package or update requires it',
153
159
  now: 'Restart immediately',
@@ -174,7 +180,9 @@ module JSS
174
180
  change_pw: 'specified',
175
181
  generate_pw: 'random',
176
182
  enable_fv2: 'fileVaultEnable',
177
- disable_fv2: 'fileVaultDisable'
183
+ disable_fv2: 'fileVaultDisable',
184
+ reset_random: 'resetRandom',
185
+ reset_pw: 'reset'
178
186
  }.freeze
179
187
 
180
188
  PACKAGE_ACTIONS = {
@@ -191,6 +199,12 @@ module JSS
191
199
  after: 'After'
192
200
  }.freeze
193
201
 
202
+ DISK_ENCRYPTION_ACTIONS = {
203
+ apply: "apply",
204
+ remediate: "remediate",
205
+ none: "none"
206
+ }
207
+
194
208
  PRINTER_ACTIONS = {
195
209
  map: 'install',
196
210
  unmap: 'uninstall'
@@ -249,14 +263,14 @@ module JSS
249
263
  }.freeze
250
264
 
251
265
  LOG_FLUSH_INTERVAL_PERIODS = {
252
- day: 'Day',
253
- days: 'Day',
254
- week: 'Week',
255
- weeks: 'Week',
256
- month: 'Month',
257
- months: 'Month',
258
- year: 'Year',
259
- years: 'Year'
266
+ day: 'Days',
267
+ days: 'Days',
268
+ week: 'Weeks',
269
+ weeks: 'Weeks',
270
+ month: 'Months',
271
+ months: 'Months',
272
+ year: 'Years',
273
+ years: 'Years'
260
274
  }.freeze
261
275
 
262
276
  # the object type for this object in
@@ -273,6 +287,83 @@ module JSS
273
287
  # How is the category stored in the API data?
274
288
  CATEGORY_DATA_TYPE = Hash
275
289
 
290
+ # Class Methods
291
+ ######################
292
+
293
+ # Flush logs for a given policy older than some number of days, weeks,
294
+ # months or years, possibly limited to one or more computers.
295
+ #
296
+ # With no parameters, flushes all logs for the policy for all computers.
297
+ #
298
+ # NOTE: Currently the API doesn't have a way to flush only failed policies.
299
+ #
300
+ # WARNING: Log flushing can take a long time, and the API call doesnt return
301
+ # until its finished. The connection timeout will be temporarily raised to
302
+ # 30 minutes, unless it's already higher.
303
+ #
304
+ # @param policy[Integer,String] The id or name of the policy to flush
305
+ #
306
+ # @param older_than[Integer] 0, 1, 2, 3, or 6
307
+ #
308
+ # @param period[Symbol] :days, :weeks, :months, or :years
309
+ #
310
+ # @param computers[Array<Integer,String>] Identifiers of the target computers
311
+ # either ids, names, SNs, macaddrs, or UDIDs. If omitted, flushes logs for
312
+ # all computers
313
+ #
314
+ # @param api [JSS::APIConnection] the API connection to use.
315
+ #
316
+ # @return [void]
317
+ #
318
+ def self.flush_logs(policy, older_than: 0, period: :days, computers: [], api: JSS.api)
319
+ orig_timeout = api.cnx.options.timeout
320
+ pol_id = valid_id policy
321
+ raise JSS::NoSuchItemError, "No Policy identified by '#{policy}'." unless pol_id
322
+
323
+ older_than = LOG_FLUSH_INTERVAL_INTEGERS[older_than]
324
+ raise JSS::InvalidDataError, "older_than must be one of these integers: #{LOG_FLUSH_INTERVAL_INTEGERS.keys.join ', '}" unless older_than
325
+
326
+ period = LOG_FLUSH_INTERVAL_PERIODS[period]
327
+ raise JSS::InvalidDataError, "period must be one of these symbols: :#{LOG_FLUSH_INTERVAL_PERIODS.keys.join ', :'}" unless period
328
+
329
+ computers = [computers] unless computers.is_a? Array
330
+
331
+ # log flushes can be really slow
332
+ api.timeout = 1800 unless orig_timeout > 1800
333
+
334
+ return api.delete_rsrc "#{LOG_FLUSH_RSRC}/policy/id/#{pol_id}/interval/#{older_than}+#{period}" if computers.empty?
335
+
336
+ flush_logs_for_specific_computers pol_id, older_than, period, computers, api
337
+ ensure
338
+ api.timeout = orig_timeout
339
+ end
340
+
341
+ # use an XML body in a DELETE request to flush logs for
342
+ # a list of computers - used by the flush_logs class method
343
+ def self.flush_logs_for_specific_computers(pol_id, older_than, period, computers, api)
344
+ # build the xml body for a DELETE request
345
+ xml_doc = REXML::Document.new JSS::APIConnection::XML_HEADER
346
+ lf = xml_doc.add_element 'logflush'
347
+ lf.add_element('log').text = 'policy'
348
+ lf.add_element('log_id').text = pol_id.to_s
349
+ lf.add_element('interval').text = "#{older_than} #{period}"
350
+ comps_elem = lf.add_element 'computers'
351
+ computers.each do |c|
352
+ id = JSS::Computer.valid_id c
353
+ next unless id
354
+
355
+ ce = comps_elem.add_element 'computer'
356
+ ce.add_element('id').text = id.to_s
357
+ end
358
+
359
+ # Do a DELETE request with a body.
360
+ api.cnx.delete(LOG_FLUSH_RSRC) do |req|
361
+ req.headers[JSS::APIConnection::HTTP_CONTENT_TYPE_HEADER] = JSS::APIConnection::MIME_XML
362
+ req.body = xml_doc.to_s
363
+ end
364
+ end
365
+ private_class_method :flush_logs_for_specific_computers
366
+
276
367
  # Attributes
277
368
  ######################
278
369
 
@@ -541,6 +632,7 @@ module JSS
541
632
 
542
633
  # @return [String] the message shown the user at policy end
543
634
  attr_reader :user_message_finish
635
+ alias user_message_end user_message_finish
544
636
 
545
637
  # @return [Hash]
546
638
  #
@@ -604,7 +696,6 @@ module JSS
604
696
 
605
697
  if @in_jss
606
698
  gen = @init_data[:general]
607
- @frequency = gen[:frequency]
608
699
  @target_drive = gen[:target_drive]
609
700
  @offline = gen[:offline]
610
701
  @enabled = gen[:enabled]
@@ -620,6 +711,10 @@ module JSS
620
711
  trigger_enrollment_complete: gen[:trigger_enrollment_complete],
621
712
  trigger_other: gen[:trigger_other]
622
713
  }
714
+ @frequency = gen[:frequency]
715
+ @retry_event = gen[:retry_event]
716
+ @retry_attempts = gen[:retry_attempts]
717
+ @notify_failed_retries = gen[:notify_on_each_failed_retry]
623
718
 
624
719
  dtl = gen[:date_time_limitations]
625
720
 
@@ -670,6 +765,7 @@ module JSS
670
765
  @disk_encryption = @init_data[:disk_encryption]
671
766
 
672
767
  @printers = @init_data[:printers]
768
+ @printers.shift
673
769
 
674
770
  # Not in jss yet
675
771
  end
@@ -735,8 +831,93 @@ module JSS
735
831
  # @return [void]
736
832
  #
737
833
  def frequency=(freq)
738
- raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ', :'}" unless FREQUENCIES.key?(freq)
739
- @frequency = FREQUENCIES[freq]
834
+ raise JSS::InvalidDataError, "New frequency must be one of :#{FREQUENCIES.keys.join ', :'}" unless FREQUENCIES.key?(freq) || FREQUENCIES.value?(freq)
835
+
836
+ freq = freq.is_a?(Symbol) ? FREQUENCIES[freq] : freq
837
+ return if freq == @frequency
838
+
839
+ @frequency = freq
840
+ @need_to_update = true
841
+ end
842
+
843
+ # @return [String] The event that causes a policy retry
844
+ def retry_event
845
+ return RETRY_EVENTS[:none] unless FREQUENCIES[:once_per_computer] == @frequency
846
+
847
+ @retry_event
848
+ end
849
+
850
+ # Set the event that causes a retry if the policy fails.
851
+ # One of the ways to turn off policy retry is to set this to :none
852
+ # The other is to set the retry_attempts to 0
853
+ #
854
+ # @param [Symbol, String] A key or value from RETRY_EVENTS
855
+ # @return [void]
856
+ #
857
+ def retry_event=(evt)
858
+ validate_retry_opt
859
+ raise JSS::InvalidDataError, "Retry event must be one of :#{RETRY_EVENTS.keys.join ', :'}" unless RETRY_EVENTS.key?(evt) || RETRY_EVENTS.value?(evt)
860
+
861
+ evt = evt.is_a?(Symbol) ? RETRY_EVENTS[evt] : evt
862
+ return if evt == @retry_event
863
+
864
+ # if the event is not 'none' and attempts is <= 0,
865
+ # set events to 1, or the API won't accept it
866
+ unless evt == RETRY_EVENTS[:none]
867
+ @retry_attempts = 1 unless @retry_attempts.positive?
868
+ end
869
+
870
+ @retry_event = evt
871
+ @need_to_update = true
872
+ end
873
+
874
+ # @return [Integer] How many times wil the policy be retried if it fails.
875
+ # -1 means no retries, otherwise, an integer from 1 to 10
876
+ def retry_attempts
877
+ return 0 unless FREQUENCIES[:once_per_computer] == @frequency
878
+
879
+ @retry_attempts
880
+ end
881
+
882
+ # Set the number of times to retry if the policy fails.
883
+ # One of the ways to turn off policy retry is to set this to 0 or -1
884
+ # The other is to set retry_event to :none
885
+ #
886
+ # @param [Integer] From -1 to 10
887
+ # @return [void]
888
+ #
889
+ def retry_attempts=(int)
890
+ validate_retry_opt
891
+ raise JSS::InvalidDataError, 'Retry attempts must be an integer from 0-10' unless int.is_a?(Integer) && (-1..10).include?(int)
892
+
893
+ # if zero or -1, turn off retries
894
+ if int <= 0
895
+ @retry_event = RETRY_EVENTS[:none]
896
+ int = -1
897
+ end
898
+ return if @retry_attempts == int
899
+
900
+ @retry_attempts = int
901
+ @need_to_update = true
902
+ end
903
+
904
+ # @return [Boolean] Should admins be notified of failed retry attempts
905
+ def notify_failed_retries?
906
+ return false unless FREQUENCIES[:once_per_computer] == @frequency
907
+
908
+ @notify_failed_retries
909
+ end
910
+ alias notify_failed_retries notify_failed_retries?
911
+ alias notify_on_each_failed_retry notify_failed_retries?
912
+
913
+ # @param bool[Boolean] Should admins be notified of failed retry attempts
914
+ # @return [void]
915
+ def notify_failed_retries=(bool)
916
+ validate_retry_opt
917
+ bool = JSS::Validate.boolean bool
918
+ return if @notify_failed_retries == bool
919
+
920
+ @notify_failed_retries = bool
740
921
  @need_to_update = true
741
922
  end
742
923
 
@@ -914,6 +1095,30 @@ module JSS
914
1095
  end
915
1096
  alias message= reboot_message=
916
1097
 
1098
+ # Set User Start Message
1099
+ #
1100
+ # @param user_message[String] Text of User Message
1101
+ #
1102
+ # @return [void] description of returned object
1103
+ def user_message_start=(message)
1104
+ raise JSS::InvalidDataError, 'User message must be a String' unless message.is_a? String
1105
+ @user_message_start = message
1106
+ @need_to_update = true
1107
+ end
1108
+
1109
+ # Set User Finish Message
1110
+ #
1111
+ # @param user_message[String] Text of User Message
1112
+ #
1113
+ # @return [void] description of returned object
1114
+ def user_message_end=(message)
1115
+ raise JSS::InvalidDataError, 'User message must be a String' unless message.is_a? String
1116
+ @user_message_finish = message
1117
+ @need_to_update = true
1118
+ end
1119
+
1120
+ alias user_message_finish= user_message_end=
1121
+
917
1122
  # Set Startup Disk
918
1123
  # Only Supports 'Specify Local Startup Disk' at the moment
919
1124
  #
@@ -1180,7 +1385,7 @@ module JSS
1180
1385
 
1181
1386
  # Remove a package from this policy by name or id
1182
1387
  #
1183
- # @param identfier [String,Integer] the name or id of the package to remove
1388
+ # @param identifier [String,Integer] the name or id of the package to remove
1184
1389
  #
1185
1390
  # @return [Array, nil] the new packages array or nil if no change
1186
1391
  #
@@ -1270,7 +1475,7 @@ module JSS
1270
1475
 
1271
1476
  # Remove a script from this policy by name or id
1272
1477
  #
1273
- # @param identfier [String,Integer] the name or id of the script to remove
1478
+ # @param identifier [String,Integer] the name or id of the script to remove
1274
1479
  #
1275
1480
  # @return [Array, nil] the new scripts array or nil if no change
1276
1481
  #
@@ -1292,6 +1497,49 @@ module JSS
1292
1497
  @directory_bindings.map { |p| p[:name] }
1293
1498
  end
1294
1499
 
1500
+ # Add a Directory Bidning to the list of directory_bindings handled by this policy.
1501
+ # If the directory binding already exists in the policy, nil is returned and
1502
+ # no changes are made.
1503
+ #
1504
+ # @param [String,Integer] identifier the name or id of the directory binding to add to this policy
1505
+ #
1506
+ # @param position [Symbol, Integer] where to add this directory binding among the list of
1507
+ # directory_bindings. Zero-based, :start and 0 are the same, as are :end and -1.
1508
+ # Defaults to :end
1509
+ #
1510
+ # @return [Array, nil] the new @directory_bindings array, nil if directory_binding was already in the policy
1511
+ #
1512
+ def add_directory_binding(identifier, **opts)
1513
+ id = validate_directory_binding_opts identifier, opts
1514
+
1515
+ return nil if @directory_bindings.map { |s| s[:id] }.include? id
1516
+
1517
+ name = JSS::DirectoryBinding.map_all_ids_to(:name, api: @api)[id]
1518
+
1519
+ directory_binding_data = {
1520
+ id: id,
1521
+ name: name
1522
+ }
1523
+
1524
+ @directory_bindings.insert opts[:position], directory_binding_data
1525
+
1526
+ @need_to_update = true
1527
+ @directory_bindings
1528
+ end
1529
+
1530
+
1531
+ # Remove a directory binding from this policy by name or id
1532
+ #
1533
+ # @param identifier [String,Integer] the name or id of the directory binding to remove
1534
+ #
1535
+ # @return [Array, nil] the new directory bindings array or nil if no change
1536
+ #
1537
+ def remove_directory_binding(identifier)
1538
+ removed = @directory_bindings.delete_if { |s| s[:id] == identifier || s[:name] == identifier }
1539
+ @need_to_update = true if removed
1540
+ removed
1541
+ end
1542
+
1295
1543
  ###### Dock items
1296
1544
 
1297
1545
  # @return [Array] the id's of the dock_items handled by the policy
@@ -1304,6 +1552,86 @@ module JSS
1304
1552
  @dock_items.map { |p| p[:name] }
1305
1553
  end
1306
1554
 
1555
+
1556
+ ###### Printers
1557
+
1558
+ # Add a specific printer object to the policy.
1559
+ #
1560
+ # @author Tyler Morgan
1561
+ #
1562
+ # @param newvalue [String,Integer] The name or the id of the printer to be added to this policy.
1563
+ #
1564
+ # @param position [Symbol, Integer] where to add this printer object among the list of printer
1565
+ # objects. Zero-based, :start and 0 are the same, as are :end and -1.
1566
+ # Defaults to :end
1567
+ #
1568
+ # @param action [Symbol] One of the PRINTER_ACTIONS symbols. What you want done with the printer object upon policy execution.
1569
+ #
1570
+ # @param make_default [TrueClass,FalseClass] Should this printer object be set to default.
1571
+ # Defaults to false
1572
+ #
1573
+ # @return [String] The new printers array or nil if the printer was already in the policy
1574
+ def add_printer(identifier, **opts)
1575
+ id = validate_printer_opts identifier, opts
1576
+
1577
+ return nil if @printers.map { |p| p[:id] }.include? id
1578
+
1579
+ name = JSS::Printer.map_all_ids_to(:name, api: @api)[id]
1580
+
1581
+ printer_data = {
1582
+ id: id,
1583
+ name: name,
1584
+ action: PRINTER_ACTIONS[opts[:action]],
1585
+ make_default: opts[:make_default]
1586
+ }
1587
+
1588
+ @printers.insert opts[:position], printer_data
1589
+
1590
+ @need_to_update = true
1591
+ @printers
1592
+ end
1593
+
1594
+
1595
+ # Remove a specific printer object from the policy.
1596
+ #
1597
+ # @author Tyler Morgan
1598
+ #
1599
+ # @param identifier [String,Integer] The name or id of the printer to be removed.
1600
+ #
1601
+ # @return [Array, nil] The new printers array or nil if no change.
1602
+ def remove_printer(identifier)
1603
+ removed = @printers.delete_if { |p| p[:id] == identifier || p[:name] == identifier }
1604
+
1605
+ @need_to_update = true
1606
+ removed
1607
+ end
1608
+
1609
+ # Add a dock item to the policy
1610
+ def add_dock_item(identifier, action)
1611
+ id = JSS::DockItem.valid_id identifier, api: @api
1612
+
1613
+ raise JSS::NoSuchItemError, "No Dock Item matches '#{identifier}'" unless id
1614
+
1615
+ raise JSS::InvalidDataError, "Action must be one of: :#{DOCK_ITEM_ACTIONS.keys.join ', :'}" unless DOCK_ITEM_ACTIONS.include? action
1616
+
1617
+ return nil if @dock_items.map { |d| d[:id] }.include? id
1618
+
1619
+ name = JSS::DockItem.map_all_ids_to(:name, api: @api)[id]
1620
+
1621
+ @dock_items << {id: id, name: name, action: DOCK_ITEM_ACTIONS[action]}
1622
+
1623
+ @need_to_update = true
1624
+ @dock_items
1625
+ end
1626
+
1627
+ # Remove a dock item from the policy
1628
+ def remove_dock_item(identifier)
1629
+ # TODO: Add validation against JSS::DockItem
1630
+ removed = @dock_items.delete_if { |d| d[:id] == identifier || d[:name] == identifier }
1631
+ @need_to_update = true if removed
1632
+ removed
1633
+ end
1634
+
1307
1635
  # @return [Array] the id's of the printers handled by the policy
1308
1636
  def printer_ids
1309
1637
  begin
@@ -1312,7 +1640,7 @@ module JSS
1312
1640
  return []
1313
1641
  end
1314
1642
  end
1315
-
1643
+
1316
1644
  # @return [Array] the names of the printers handled by the policy
1317
1645
  def printer_names
1318
1646
  begin
@@ -1322,6 +1650,130 @@ module JSS
1322
1650
  end
1323
1651
  end
1324
1652
 
1653
+
1654
+
1655
+ ###### Disk Encryption
1656
+
1657
+ # Sets the Disk Encryption application to "Remediate" and sets the remediation key type to individual.
1658
+ #
1659
+ # @author Tyler Morgan
1660
+ #
1661
+ # @return [Void]
1662
+ #
1663
+ def reissue_key()
1664
+ if @disk_encryption[:action] != DISK_ENCRYPTION_ACTIONS[:remediate]
1665
+ # Setting New Action
1666
+ hash = {
1667
+ action: DISK_ENCRYPTION_ACTIONS[:remediate],
1668
+ remediate_key_type: "Individual"
1669
+ }
1670
+
1671
+ @disk_encryption = hash
1672
+ @need_to_update = true
1673
+
1674
+ else
1675
+ # Update
1676
+ return
1677
+ end
1678
+
1679
+ end
1680
+
1681
+
1682
+ # Sets the Disk Encryption application to "Apply" and sets the correct disk encryption configuration ID using either the name or id.
1683
+ #
1684
+ # @author Tyler Morgan
1685
+ #
1686
+ # @return [Void]
1687
+ #
1688
+ def apply_encryption_configuration(identifier)
1689
+
1690
+ id = JSS::DiskEncryptionConfiguration.valid_id identifier
1691
+
1692
+ return if id.nil?
1693
+
1694
+ hash = {
1695
+ action: DISK_ENCRYPTION_ACTIONS[:apply],
1696
+ disk_encryption_configuration_id: id,
1697
+ auth_restart: false
1698
+ }
1699
+
1700
+ @disk_encryption = hash
1701
+ @need_to_update = true
1702
+ end
1703
+
1704
+
1705
+ # Removes the Disk Encryption settings associated with this specific policy.
1706
+ #
1707
+ # @author Tyler Morgan
1708
+ #
1709
+ # @return [Void]
1710
+ #
1711
+ def remove_encryption_configuration()
1712
+ hash = {
1713
+ action: DISK_ENCRYPTION_ACTIONS[:none]
1714
+ }
1715
+
1716
+ @disk_encryption = hash
1717
+ @need_to_update = true
1718
+ end
1719
+
1720
+ # Interact with management account settings
1721
+ #
1722
+ # @param action [Key] one of the MGMT_ACCOUNT_ACTIONS keys
1723
+ #
1724
+ # @return The current specified management settings.
1725
+ #
1726
+ # Reference: https://developer.jamf.com/documentation#resources-with-passwords
1727
+ #
1728
+ def set_management_account(action, **opts)
1729
+ # TODO: Add proper error handling
1730
+ raise JSS::InvalidDataError, "Action must be one of: :#{MGMT_ACCOUNT_ACTIONS.keys.join ', :'}" unless MGMT_ACCOUNT_ACTIONS.include? action
1731
+
1732
+ management_data = {}
1733
+
1734
+ if action == :change_pw || action == :reset_pw
1735
+ raise JSS::MissingDataError, ":password must be provided when changing management account password" if opts[:password].nil?
1736
+
1737
+ management_data = {
1738
+ action: MGMT_ACCOUNT_ACTIONS[action],
1739
+ managed_password: opts[:password]
1740
+ }
1741
+ elsif action == :reset_random || action == :generate_pw
1742
+ raise JSS::MissingDataError, ":password_length must be provided when setting a random password" if opts[:password_length].nil?
1743
+ raise JSS::InvalidDataError, ":password_length must be an Integer" unless opts[:password_length].is_a? Integer
1744
+
1745
+ management_data = {
1746
+ action: MGMT_ACCOUNT_ACTIONS[action],
1747
+ managed_password_length: opts[:password_length]
1748
+ }
1749
+ else
1750
+ management_data = {
1751
+ action: MGMT_ACCOUNT_ACTIONS[action]
1752
+ }
1753
+ end
1754
+
1755
+ @management_account = management_data
1756
+
1757
+ @need_to_update = true
1758
+
1759
+ @management_account
1760
+
1761
+ end
1762
+
1763
+ # Check if management password matches provided password
1764
+ #
1765
+ # @param password[String] the password that is SHA256'ed to compare to the one from the API.
1766
+ #
1767
+ # @return [Boolean] The result of the comparison of the management password and provided text.
1768
+ #
1769
+ def verify_management_password(password)
1770
+ raise JSS::InvalidDataError, "Management password must be a string." unless password.is_a? String
1771
+
1772
+ raise JSS::UnsupportedError, "'#{@management_account[:action].to_s}' does not support management passwords." unless @management_account[:action] == MGMT_ACCOUNT_ACTIONS[:change_pw] || @management_account[:action] == MGMT_ACCOUNT_ACTIONS[:reset_pw]
1773
+
1774
+ return Digest::SHA256.hexdigest(password).to_s == @management_account[:managed_password_sha256].to_s
1775
+ end
1776
+
1325
1777
  ###### Actions
1326
1778
 
1327
1779
  # Try to execute this policy on this machine.
@@ -1340,34 +1792,37 @@ module JSS
1340
1792
  end
1341
1793
  alias execute run
1342
1794
 
1343
- # Flush all policy logs for this policy older than
1344
- # some number of days, weeks, months or years.
1795
+ # Flush logs for this policy older than
1796
+ # some number of days, weeks, months or years, possibly limited to
1797
+ # one or more computers
1345
1798
  #
1346
- # With no parameters, flushes all logs
1799
+ # With no parameters, flushes all logs for all computers
1347
1800
  #
1348
- # NOTE: Currently the API doesn't have a way to
1349
- # flush only failed policies.
1801
+ # NOTE: Currently the API doesn't have a way to flush only failed policies.
1802
+ #
1803
+ # WARNING: Log flushing can take a long time, and the API call doesnt return
1804
+ # until its finished. The connection timeout will be temporarily raised to
1805
+ # 30 minutes, unless it's already higher.
1350
1806
  #
1351
1807
  # @param older_than[Integer] 0, 1, 2, 3, or 6
1352
1808
  #
1353
1809
  # @param period[Symbol] :days, :weeks, :months, or :years
1354
1810
  #
1811
+ # @param computers[Array<Integer,String>] Identifiers of the target computers
1812
+ # either ids, names, SNs, macaddrs, or UDIDs
1813
+ #
1355
1814
  # @return [void]
1356
1815
  #
1357
- def flush_logs(older_than: 0, period: :days)
1816
+ def flush_logs(older_than: 0, period: :days, computers: [])
1358
1817
  raise JSS::NoSuchItemError, "Policy doesn't exist in the JSS. Use #create first." unless @in_jss
1359
1818
 
1360
- unless LOG_FLUSH_INTERVAL_INTEGERS.key?(older_than)
1361
- raise JSS::InvalidDataError, "older_than must be one of these integers: #{LOG_FLUSH_INTERVAL_INTEGERS.keys.join ', '}"
1362
- end
1363
-
1364
- unless LOG_FLUSH_INTERVAL_PERIODS.key?(period)
1365
- raise JSS::InvalidDataError, "period must be one of these symbols: :#{LOG_FLUSH_INTERVAL_PERIODS.keys.join ', :'}"
1366
- end
1367
-
1368
- interval = "#{LOG_FLUSH_INTERVAL_INTEGERS[older_than]}+#{LOG_FLUSH_INTERVAL_PERIODS[period]}"
1369
-
1370
- @api.delete_rsrc "#{LOG_FLUSH_RSRC}/policy/id/#{@id}/interval/#{interval}"
1819
+ JSS::Policy.flush_logs(
1820
+ @id,
1821
+ older_than: older_than,
1822
+ period: period,
1823
+ computers: computers,
1824
+ api: @api
1825
+ )
1371
1826
  end
1372
1827
 
1373
1828
  # Private Instance Methods
@@ -1375,6 +1830,17 @@ module JSS
1375
1830
 
1376
1831
  private
1377
1832
 
1833
+ # raise an error if a trying to set retry options when
1834
+ # frequency is not 'once per comptuer'
1835
+ #
1836
+ # @return [void]
1837
+ #
1838
+ def validate_retry_opt
1839
+ return if FREQUENCIES[:once_per_computer] == @frequency
1840
+
1841
+ raise JSS::UnsupportedError, 'Policy retry is only available when frequency is set to :once_per_computer'
1842
+ end
1843
+
1378
1844
  # raise an error if a package being added isn't valid
1379
1845
  #
1380
1846
  # @see #add_package
@@ -1439,6 +1905,64 @@ module JSS
1439
1905
  id
1440
1906
  end
1441
1907
 
1908
+ # raise an error if the directory binding being added isn't valid
1909
+ #
1910
+ # @see #add_directory_binding
1911
+ #
1912
+ # @return [Integer, nil] the valid id for the package
1913
+ #
1914
+ def validate_directory_binding_opts(identifier, opts)
1915
+ opts[:position] ||= -1
1916
+
1917
+ opts[:position] =
1918
+ case opts[:position]
1919
+ when :start then 0
1920
+ when :end then -1
1921
+ else JSS::Validate.integer(opts[:position])
1922
+ end
1923
+
1924
+ # if the given position is past the end, set it to -1 (the end)
1925
+ opts[:position] = -1 if opts[:position] > @directory_bindings.size
1926
+
1927
+ id = JSS::DirectoryBinding.valid_id identifier, api: @api
1928
+ raise JSS::NoSuchItemError, "No directory binding matches '#{identifier}'" unless id
1929
+ id
1930
+ end
1931
+
1932
+ # Raises an error if the printer being added isn't valid, additionally checks the options and sets defaults where possible.
1933
+ #
1934
+ # @see #add_printer
1935
+ #
1936
+ # @return [Integer, nil] the valid id for the package
1937
+ #
1938
+ def validate_printer_opts(identifier, opts)
1939
+ opts[:position] ||= -1
1940
+
1941
+ opts[:position] =
1942
+ case opts[:position]
1943
+ when :start then 0
1944
+ when :end then -1
1945
+ else JSS::Validate.integer(opts[:position])
1946
+ end
1947
+
1948
+ # If the given position is past the end, set it to -1 (the end)
1949
+ opts[:position] = -1 if opts[:position] > @printers.size
1950
+
1951
+ # Checks if action to be done with the printer object is provided and valid.
1952
+ raise JSS::MissingDataError, "action must be provided, must be one of :#{PRINTER_ACTIONS.keys.join(':,')}." if opts[:action].nil?
1953
+ raise JSS::InvalidDataError, "action must be one of :#{PRINTER_ACTIONS.keys.join(',:')}." unless PRINTER_ACTIONS.keys.include? opts[:action]
1954
+
1955
+
1956
+ # Checks if the make_default option is valid, and sets the default if needed.
1957
+ raise JSS::InvalidDataError, "make_default must be either true or false." unless opts[:make_default].is_a?(TrueClass) || opts[:make_default].is_a?(FalseClass) || opts[:make_default].nil?
1958
+
1959
+ opts[:make_default] = false if opts[:make_default].nil?
1960
+
1961
+ id = JSS::Printer.valid_id identifier, api: @api
1962
+ raise JSS::NoSuchItemError, "No printer matches '#{identifier}'" unless id
1963
+ id
1964
+ end
1965
+
1442
1966
  def rest_xml
1443
1967
  doc = REXML::Document.new APIConnection::XML_HEADER
1444
1968
  obj = doc.add_element RSRC_OBJECT_KEY.to_s
@@ -1447,6 +1971,10 @@ module JSS
1447
1971
  general.add_element('name').text = @name
1448
1972
  general.add_element('enabled').text = @enabled
1449
1973
  general.add_element('frequency').text = @frequency
1974
+ general.add_element('retry_event').text = @retry_event
1975
+ general.add_element('retry_attempts').text = @retry_attempts.to_s
1976
+ general.add_element('notify_on_each_failed_retry').text = @notify_failed_retries.to_s
1977
+
1450
1978
  general.add_element('target_drive').text = @target_drive
1451
1979
  general.add_element('offline').text = @offline
1452
1980
 
@@ -1475,6 +2003,22 @@ module JSS
1475
2003
  maint.add_element('user_cache').text = @user_cache.to_s
1476
2004
  maint.add_element('verify').text = @verify_startup_disk.to_s
1477
2005
 
2006
+ acct_maint = obj.add_element 'account_maintenance'
2007
+
2008
+ mgmt_acct = acct_maint.add_element 'management_account'
2009
+ JSS.hash_to_rexml_array(@management_account).each { |x| mgmt_acct << x }
2010
+
2011
+ directory_bindings = acct_maint.add_element 'directory_bindings'
2012
+ @directory_bindings.each do |b|
2013
+ directory_binding = directory_bindings.add_element 'binding'
2014
+ dbdeets = JSS.hash_to_rexml_array b
2015
+ dbdeets.each { |d| directory_binding << d }
2016
+ end
2017
+
2018
+ user_interaction = obj.add_element 'user_interaction'
2019
+ user_interaction.add_element('message_start').text = @user_message_start.to_s
2020
+ user_interaction.add_element('message_finish').text = @user_message_finish.to_s
2021
+
1478
2022
  files_processes = obj.add_element 'files_processes'
1479
2023
  JSS.hash_to_rexml_array(@files_processes).each { |f| files_processes << f }
1480
2024
 
@@ -1493,6 +2037,26 @@ module JSS
1493
2037
  sdeets.each { |d| script << d }
1494
2038
  end
1495
2039
 
2040
+ disk_encryption = obj.add_element 'disk_encryption'
2041
+
2042
+ @disk_encryption.each do |k,v|
2043
+ disk_encryption.add_element(k.to_s).text = v.to_s
2044
+ end
2045
+
2046
+ printers = obj.add_element 'printers'
2047
+ @printers.each do |pr|
2048
+ printer = printers.add_element 'printer'
2049
+ pdeets = JSS.hash_to_rexml_array pr
2050
+ pdeets.each { |d| printer << d }
2051
+ end
2052
+
2053
+ dock_items = obj.add_element 'dock_items'
2054
+ @dock_items.each do |d|
2055
+ dock_item = dock_items.add_element 'dock_item'
2056
+ ddeets = JSS.hash_to_rexml_array d
2057
+ ddeets.each { |de| dock_item << de }
2058
+ end
2059
+
1496
2060
  add_self_service_xml doc
1497
2061
  add_site_to_xml doc
1498
2062