ruby-jss 1.2.10 → 1.5.2

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 (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