ruby-jss 2.1.0 → 3.0.0b1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 970c4289986bb3f0b3ea9a60b31777a29b4c927879a3204d28c3f9bdd46a2a9f
4
- data.tar.gz: f75ae4ae8ba5bd0cfadb065c62f495fcb4eea86b2473aa5d8357569e9ce49059
3
+ metadata.gz: a7af617b6ac8bb27cbc7d5c790a2de9fa788fb3d426ae062581654fbc9f77c5f
4
+ data.tar.gz: f5dfabfdde6c466951862fdc458321ad4f9384293e1fe773fb78a0ca80e51283
5
5
  SHA512:
6
- metadata.gz: b86acc435cc02cdd0976361305331b4bf98b176fd25c8accb791cfba794c7bdf6d42d94d62cc76b11cb510e9c3ac23495ac99aa244e76a39e5e6e061aeeae4a8
7
- data.tar.gz: 31f95548e2cb64bedb1781977222c705183c3e866ce8c8a9fb5532dfdbdeabe11d59faa92818c8506d5235d2fc868d1eb58d949b17ecc5f6c3d66bafde910750
6
+ metadata.gz: 85fd8c1218206a35545cc034058996a5b03183f29ac443cf65735c547975afcedaf406f74099f610380a1b72272db5240c2427ab4e87201ef5d9ec7ceffd2f29
7
+ data.tar.gz: a04b06cbcb9858b06dfe738ced2943e37b6a12088da394599b6ccfae66ac0a9c1bc82a264dac02c0379308141d4cc9795594c19639f473e5684cdd62e48db368
data/CHANGES.md CHANGED
@@ -14,6 +14,33 @@ __Please update all installations of ruby-jss to at least v1.6.0.__
14
14
 
15
15
  Many many thanks to actae0n of Blacksun Hackers Club for reporting this issue and providing examples of how it could be exploited.
16
16
 
17
+ --------
18
+
19
+ ## \[UNRELEASED]
20
+
21
+ ### Added
22
+ - Jamf::Policy.flush_logs_for_computers: formerly private class method, now public and used for flushing policy logs for specific computers.
23
+
24
+ ### Fixed
25
+ - Fix bug in MDM enable_lost_mode instance method, and make the default behavior match the API
26
+ - Specify the connection instance when validating ids in MacOSManagedUpdates
27
+ - Send mandatory field 'name' with a MobileDeviceApplication request (Thanks @yanniks!)
28
+ - Policy Log Flushing now reflects API limitation: You can flush logs for a policy for all computers, or for a computer for all policies, but not specific policies for specific computers. See Jamf::Policy.flush_logs and Jamf::Policy.flush_logs_for_computers
29
+ - A validation method wasn't passing cnx param correctly.
30
+
31
+ ### Changed
32
+ - MacOSManagedUpdates.send_managed_os_update takes symbols as the updateAction
33
+
34
+ ## \[2.1.1] - 2022-11-07
35
+
36
+ ### Fixed & Deprecated
37
+
38
+ - The classic API no longer includes SHA256 hashes of various passwords - the data value is there, but only contains a string of asterisks. As such, ruby-jss can no longer use those to validate some passwords before trying to use them. The methods doing so are still present, but only return `true`. If an incorrect password is given, the underlying process that uses it will fail on its own.
39
+ These methods will be removed in a future version of ruby-jss:
40
+ - `Jamf::DistributionPoint#check_pw` Used mostly by the `Jamf::DistributionPoint#mount` method
41
+ - `Jamf::Policy.verify_management_password`
42
+
43
+
17
44
  ## \[2.1.0] - 2022-10-10
18
45
 
19
46
  ### Added
@@ -1158,15 +1158,29 @@ module Jamf
1158
1158
  @need_to_update = true
1159
1159
  end
1160
1160
 
1161
- # flush the logs for this computer in a given policy
1162
- # @see Jamf::Policy.flush_logs
1161
+ # Flush all policy logs for this computer older than a given time period.
1163
1162
  #
1164
- def flush_policy_logs(policy, older_than: 0, period: :days)
1165
- Jamf::Policy.flush_logs(
1166
- policy,
1163
+ # IMPORTANT: from the Jamf Developer Site:
1164
+ # The ability to flush logs is currently only supported for flushing all logs
1165
+ # for a given policy or all logs for a given computer. There is no support for
1166
+ # flushing logs for a given policy and computer combination.
1167
+ #
1168
+ # With no parameters, will flush all logs for the computer
1169
+ #
1170
+ # NOTE: Currently the API doesn't have a way to flush only failed policies.
1171
+ #
1172
+ # @param older_than[Integer] 0, 1, 2, 3, or 6
1173
+ #
1174
+ # @param period[Symbol] :days, :weeks, :months, or :years
1175
+ #
1176
+ # @see Jamf::Policy.flush_logs_for_computers
1177
+ #
1178
+ def flush_policy_logs(older_than: 0, period: :days)
1179
+ Jamf::Policy.flush_logs_for_computers(
1180
+ [@id],
1167
1181
  older_than: older_than,
1168
1182
  period: period,
1169
- computers: [@id], cnx: @cnx
1183
+ cnx: @cnx
1170
1184
  )
1171
1185
  end
1172
1186
 
@@ -259,8 +259,8 @@ module Jamf
259
259
  attr_reader :ssh_password_sha256
260
260
 
261
261
  def initialize(**args)
262
- super
263
-
262
+ super
263
+
264
264
  @ip_address = @init_data[:ip_address]
265
265
  @local_path = @init_data[:local_path]
266
266
  @enable_load_balancing = @init_data[:enable_load_balancing]
@@ -294,44 +294,28 @@ module Jamf
294
294
 
295
295
  @port = @init_data[:ssh_password]
296
296
 
297
- # Note, as of Casper 9.3:
298
- # :management_password_md5=>"xxxxx"
299
- # and
300
- # :management_password_sha256=> "xxxxxxxxxx"
301
- # Are the read/write password
302
- #
303
- # An empty passwd is
304
- # MD5 = d41d8cd98f00b204e9800998ecf8427e
305
- # SHA256 = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
306
- #
307
- # Seemms the read-only pw isn't available in the API
308
-
309
297
  # if we mount for fileservice, where's the mountpoint?
310
298
  @mountpoint = DEFAULT_MOUNTPOINT_DIR + "#{DEFAULT_MOUNTPOINT_PREFIX}#{@id}"
311
299
  end # init
312
300
 
313
- # Check the validity of a password.
301
+ # @deprecated The API no longer sends SHA256 hashed password data, and instead
302
+ # only has a string of asterisks, meaning we can no longer use it to validate
303
+ # passwords before attempting to use them. Instead, the processes that use
304
+ # them, e.g. mounting a Dist. Point, will fail on their own if the pw is not
305
+ # valid.
314
306
  #
315
- # @param user[Symbol] one of :ro, :rw, :ssh, :http
307
+ # This method remains defined for backward-compatibility with any existing
308
+ # code that calls it. but it will always return true. It will be removed in
309
+ # a future version
316
310
  #
317
- # @param pw[String] the password to check for the given user
311
+ # @param user[Symbol] ignored
318
312
  #
319
- # @return [Boolean,Nil] was the password correct?
320
- # nil is returned if there is no password set in the JSS.
313
+ # @param pw[String] ignored
321
314
  #
322
- def check_pw(user, pw)
323
- raise Jamf::InvalidDataError, 'The first parameter must be one of :ro, :rw, :ssh, :http' unless %i[ro rw ssh http].include? user
324
-
325
- sha256 = case user
326
- when :rw then @read_write_password_sha256
327
- when :ro then @read_only_password_sha256
328
- when :http then @http_password_sha256
329
- when :ssh then @ssh_password_sha256
330
- end # case
331
-
332
- return nil if sha256 == EMPTY_PW_256
333
-
334
- sha256 == Digest::SHA2.new(256).update(pw).to_s
315
+ # @return [TrueClass] Allow the process calling this to continue.
316
+ #
317
+ def check_pw(_user = nil, _pw = nil)
318
+ true
335
319
  end
336
320
 
337
321
  # Check to see if this dist point is reachable for downloads (read-only)
@@ -350,7 +334,6 @@ module Jamf
350
334
  def reachable_for_download?(pw = '', check_http = true)
351
335
  return :http if check_http && http_reachable?(pw)
352
336
  return :mountable if mounted?
353
- return false unless check_pw :ro, pw
354
337
 
355
338
  begin
356
339
  mount pw, :ro
@@ -371,7 +354,6 @@ module Jamf
371
354
  #
372
355
  def reachable_for_upload?(pw)
373
356
  return :mountable if mounted?
374
- return false unless check_pw :rw, pw
375
357
 
376
358
  begin
377
359
  mount pw, :rw
@@ -413,12 +395,6 @@ module Jamf
413
395
  pw
414
396
  end
415
397
 
416
- pwok = check_pw(access, password)
417
- unless pwok
418
- msg = pwok.nil? ? "No #{access} password set in the JSS" : "Incorrect password for #{access} account"
419
- raise Jamf::InvalidDataError, msg
420
- end
421
-
422
398
  username = access == :ro ? @read_only_username : @read_write_username
423
399
 
424
400
  safe_pw = CGI.escape password.to_s
@@ -481,7 +457,7 @@ module Jamf
481
457
  private
482
458
 
483
459
  # can the dp be reached for http downloads?
484
- def http_reachable?(pw)
460
+ def http_reachable?(pw = nil)
485
461
  return false unless http_downloads_enabled
486
462
 
487
463
  url =
@@ -476,7 +476,7 @@ module Jamf
476
476
  raise Jamf::UnmanagedError, "#{self} with id #{target_id} is not managed. Cannot send command." unless all_mgd.include? target_id
477
477
  end
478
478
  end # unles
479
-
479
+
480
480
  targets
481
481
  end
482
482
 
@@ -860,7 +860,7 @@ module Jamf
860
860
  cnx = api if api
861
861
 
862
862
  unless WALLPAPER_LOCATIONS.keys.include? wallpaper_setting
863
- raise ArgumentError,
863
+ raise ArgumentError,
864
864
  "wallpaper_setting must be one of: :#{WALLPAPER_LOCATIONS.keys.join ', :'}"
865
865
  end
866
866
 
@@ -999,7 +999,7 @@ module Jamf
999
999
  #
1000
1000
  # @param play_sound[Boolean] Play a sound when entering lost mode
1001
1001
  #
1002
- # @param enforce_lost_mode[Boolean] Re-enabled lost mode when re-enrolled after wipe.
1002
+ # @param enforce_lost_mode[Boolean] Re-enable lost mode when re-enrolled after wipe. Default is false
1003
1003
  #
1004
1004
  # @param cnx [Jamf::Connection] the API thru which to send the command
1005
1005
  #
@@ -1011,7 +1011,7 @@ module Jamf
1011
1011
  phone: nil,
1012
1012
  footnote: nil,
1013
1013
  play_sound: false,
1014
- enforce_lost_mode: true,
1014
+ enforce_lost_mode: false,
1015
1015
  api: nil,
1016
1016
  cnx: Jamf.cnx
1017
1017
  )
@@ -1081,7 +1081,7 @@ module Jamf
1081
1081
 
1082
1082
  status = FLUSHABLE_STATUSES[status]
1083
1083
 
1084
- # TODO: add 'unmanaged_ok:' param to raw_targets_to_ids method, so that we can
1084
+ # TODO: add 'unmanaged_ok:' param to raw_targets_to_ids method, so that we can
1085
1085
  # use this to flush commands for unmanaged machines.
1086
1086
  target_ids = raw_targets_to_ids targets, cnx: cnx, expand_groups: false, unmanaged_ok: true
1087
1087
 
@@ -1378,21 +1378,21 @@ module Jamf
1378
1378
  #
1379
1379
  # @param play_sound[Boolean] Play a sound when entering lost mode
1380
1380
  #
1381
- # @param enforce_lost_mode[Boolean] Re-enabled lost mode when re-enrolled after wipe.
1381
+ # @param enforce_lost_mode[Boolean] Re-enable lost mode when re-enrolled after wipe. Default is false
1382
1382
  #
1383
1383
  # @return (see .send_mdm_command)
1384
1384
  #
1385
1385
  def enable_lost_mode(
1386
1386
  message: nil,
1387
- phone_number: nil,
1387
+ phone: nil,
1388
1388
  footnote: nil,
1389
- enforce_lost_mode: true,
1389
+ enforce_lost_mode: false,
1390
1390
  play_sound: false
1391
1391
  )
1392
1392
  self.class.enable_lost_mode(
1393
1393
  @id,
1394
1394
  message: message,
1395
- phone_number: phone_number,
1395
+ phone: phone,
1396
1396
  footnote: footnote,
1397
1397
  play_sound: play_sound,
1398
1398
  enforce_lost_mode: enforce_lost_mode, cnx: @cnx
@@ -544,6 +544,7 @@ module Jamf
544
544
  doc = REXML::Document.new Jamf::Connection::XML_HEADER
545
545
  obj = doc.add_element self.class::RSRC_OBJECT_KEY.to_s
546
546
  gen = obj.add_element 'general'
547
+ gen.add_element('name').text = @display_name
547
548
  gen.add_element('display_name').text = @display_name
548
549
  gen.add_element('description').text = @description
549
550
  gen.add_element('os_type').text = @os_type
@@ -166,7 +166,7 @@ module Jamf
166
166
  selected: 'Currently Selected Startup Disk (No Bless)',
167
167
  netboot: 'NetBoot',
168
168
  os_installer: 'inPlaceOSUpgradeDirectory'
169
- }.freeze # Note: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
169
+ }.freeze # NOTE: any other value in :specify_startup is a path to some other drive to boot from, e.g. /Volumes/Foo
170
170
 
171
171
  ACCOUNT_ACTIONS = {
172
172
  create: 'Create',
@@ -200,9 +200,9 @@ module Jamf
200
200
  }.freeze
201
201
 
202
202
  DISK_ENCRYPTION_ACTIONS = {
203
- apply: "apply",
204
- remediate: "remediate",
205
- none: "none"
203
+ apply: 'apply',
204
+ remediate: 'remediate',
205
+ none: 'none'
206
206
  }
207
207
 
208
208
  PRINTER_ACTIONS = {
@@ -293,10 +293,19 @@ module Jamf
293
293
  # Class Methods
294
294
  ######################
295
295
 
296
- # Flush logs for a given policy older than some number of days, weeks,
297
- # months or years, possibly limited to one or more computers.
296
+ # Flush logs for a given policy older than a given time period.
297
+ # This flushes the logs of the given policy for all computers.
298
298
  #
299
- # With no parameters, flushes all logs for the policy for all computers.
299
+ # IMPORTANT: from the Jamf Developer Site:
300
+ # The ability to flush logs is currently only supported for flushing all logs
301
+ # for a given policy or all logs for a given computer. There is no support for
302
+ # flushing logs for a given policy and computer combination.
303
+ #
304
+ # (See .flush_logs_for_computers to to flush all logs for given computers)
305
+ #
306
+ # With no parameters, flushes all logs for the policy.
307
+ #
308
+ # Without older_than: and period:, will flush all logs for the policy
300
309
  #
301
310
  # NOTE: Currently the API doesn't have a way to flush only failed policies.
302
311
  #
@@ -306,52 +315,74 @@ module Jamf
306
315
  #
307
316
  # @param policy[Integer,String] The id or name of the policy to flush
308
317
  #
309
- # @param older_than[Integer] 0, 1, 2, 3, or 6
318
+ # @param older_than[Integer] 0, 1, 2, 3, or 6, defaults to 0
310
319
  #
311
- # @param period[Symbol] :days, :weeks, :months, or :years
312
- #
313
- # @param computers[Array<Integer,String>] Identifiers of the target computers
314
- # either ids, names, SNs, macaddrs, or UDIDs. If omitted, flushes logs for
315
- # all computers
320
+ # @param period[Symbol] :days, :weeks, :months, or :years, defaults to :days
316
321
  #
317
322
  # @param cnx [Jamf::Connection] the API connection to use.
318
323
  #
319
324
  # @return [void]
320
325
  #
321
- def self.flush_logs(policy, older_than: 0, period: :days, computers: [], api: nil, cnx: Jamf.cnx)
326
+ def self.flush_logs(policy, older_than: 0, period: :days, api: nil, cnx: Jamf.cnx)
322
327
  cnx = api if api
323
328
 
324
- orig_timeout = cnx.timeout
325
329
  pol_id = valid_id policy, cnx: cnx
326
330
  raise Jamf::NoSuchItemError, "No Policy identified by '#{policy}'." unless pol_id
327
331
 
328
- older_than = LOG_FLUSH_INTERVAL_INTEGERS[older_than]
329
- raise Jamf::InvalidDataError, "older_than must be one of these integers: #{LOG_FLUSH_INTERVAL_INTEGERS.keys.join ', '}" unless older_than
330
-
331
- period = LOG_FLUSH_INTERVAL_PERIODS[period]
332
- raise Jamf::InvalidDataError, "period must be one of these symbols: :#{LOG_FLUSH_INTERVAL_PERIODS.keys.join ', :'}" unless period
333
-
334
- computers = [computers] unless computers.is_a? Array
332
+ older_than, period = validate_log_flush_params(older_than, period)
335
333
 
336
334
  # log flushes can be really slow
335
+ orig_timeout = cnx.timeout
337
336
  cnx.timeout = 1800 unless orig_timeout && orig_timeout > 1800
338
337
 
339
- return cnx.c_delete "#{LOG_FLUSH_RSRC}/policy/id/#{pol_id}/interval/#{older_than}+#{period}" if computers.empty?
340
-
341
- flush_logs_for_specific_computers pol_id, older_than, period, computers, cnx
338
+ cnx.c_delete "#{LOG_FLUSH_RSRC}/policy/id/#{pol_id}/interval/#{older_than}+#{period}"
342
339
  ensure
343
340
  cnx.timeout = orig_timeout
344
341
  end
345
342
 
346
- # use an XML body in a DELETE request to flush logs for
347
- # a list of computers - used by the flush_logs class method
348
- def self.flush_logs_for_specific_computers(pol_id, older_than, period, computers, cnx)
343
+ # Flush policy logs for specific computers older than a given time period.
344
+ # This flushes the logs for all policies for these computers.
345
+ #
346
+ # IMPORTANT: from the Jamf Developer Site:
347
+ # The ability to flush logs is currently only supported for flushing all logs
348
+ # for a given policy or all logs for a given computer. There is no support for
349
+ # flushing logs for a given policy and computer combination.
350
+ #
351
+ # (See .flush_logs to to flush all logs for a given policy)
352
+ #
353
+ # Without older_than: and period:, will flush all logs for the computers
354
+ #
355
+ # NOTE: Currently the API doesn't have a way to flush only failed policies.
356
+ #
357
+ # WARNING: Log flushing can take a long time, and the API call doesnt return
358
+ # until its finished. The connection timeout will be temporarily raised to
359
+ # 30 minutes, unless it's already higher.
360
+ #
361
+ # @param computers[Array<Integer,String>] Identifiers of the target computers
362
+ # either ids, names, SNs, macaddrs, or UDIDs.
363
+ #
364
+ # @param older_than[Integer] 0, 1, 2, 3, or 6, defaults to 0
365
+ #
366
+ # @param period[Symbol] :days, :weeks, :months, or :years, defaults to :days
367
+ #
368
+ # @param cnx [Jamf::Connection] the API connection to use.
369
+ #
370
+ # @return [void]
371
+ #
372
+ def self.flush_logs_for_computers(computers = [], older_than: 0, period: :days, api: nil, cnx: Jamf.cnx)
373
+ cnx = api if api
374
+
375
+ computers = [computers] unless computers.is_a? Array
376
+ raise JSS::InvalidDataError, 'One or more computers must be specified' if computers.empty?
377
+
378
+ older_than, period = validate_log_flush_params(older_than, period)
379
+
349
380
  # build the xml body for a DELETE request
350
381
  xml_doc = REXML::Document.new Jamf::Connection::XML_HEADER
351
382
  lf = xml_doc.add_element 'logflush'
352
383
  lf.add_element('log').text = 'policy'
353
- lf.add_element('log_id').text = pol_id.to_s
354
384
  lf.add_element('interval').text = "#{older_than} #{period}"
385
+
355
386
  comps_elem = lf.add_element 'computers'
356
387
  computers.each do |c|
357
388
  id = Jamf::Computer.valid_id c, cnx: cnx
@@ -361,13 +392,41 @@ module Jamf
361
392
  ce.add_element('id').text = id.to_s
362
393
  end
363
394
 
395
+ # for debugging the xml...
396
+ #
397
+ # formatter = REXML::Formatters::Pretty.new(2)
398
+ # formatter.compact = true
399
+ # formatter.write(xml_doc, $stdout)
400
+ # puts
401
+ # return
402
+
403
+ # log flushes can be really slow
404
+ orig_timeout = cnx.timeout
405
+ cnx.timeout = 1800 unless orig_timeout && orig_timeout > 1800
406
+
364
407
  # Do a DELETE request with a body.
365
- cnx.delete(LOG_FLUSH_RSRC) do |req|
408
+ resp = cnx.c_cnx.delete(LOG_FLUSH_RSRC) do |req|
366
409
  req.headers[Jamf::Connection::HTTP_CONTENT_TYPE_HEADER] = Jamf::Connection::MIME_XML
367
410
  req.body = xml_doc.to_s
368
411
  end
412
+
413
+ resp.body
414
+ ensure
415
+ cnx.timeout = orig_timeout
369
416
  end
370
- private_class_method :flush_logs_for_specific_computers
417
+
418
+ # validate the logflush params
419
+ # @return [Array<String>]
420
+ def self.validate_log_flush_params(older_than, period)
421
+ older_than = LOG_FLUSH_INTERVAL_INTEGERS[older_than]
422
+ raise Jamf::InvalidDataError, "older_than must be one of these integers: #{LOG_FLUSH_INTERVAL_INTEGERS.keys.join ', '}" unless older_than
423
+
424
+ period = LOG_FLUSH_INTERVAL_PERIODS[period]
425
+ raise Jamf::InvalidDataError, "period must be one of these symbols: :#{LOG_FLUSH_INTERVAL_PERIODS.keys.join ', :'}" unless period
426
+
427
+ [older_than, period]
428
+ end
429
+ private_class_method :validate_log_flush_params
371
430
 
372
431
  # Attributes
373
432
  ######################
@@ -751,7 +810,7 @@ module Jamf
751
810
  @management_account = amaint[:management_account]
752
811
  @accounts = amaint[:accounts]
753
812
 
754
- @packages = @init_data[:package_configuration][:packages] ? @init_data[:package_configuration][:packages] : []
813
+ @packages = @init_data[:package_configuration][:packages] || []
755
814
 
756
815
  @scripts = @init_data[:scripts]
757
816
 
@@ -815,6 +874,7 @@ module Jamf
815
874
  #
816
875
  def enabled=(new_val)
817
876
  return if @enabled == new_val
877
+
818
878
  @enabled = Jamf::Validate.boolean new_val
819
879
  @need_to_update = true
820
880
  end
@@ -868,9 +928,7 @@ module Jamf
868
928
 
869
929
  # if the event is not 'none' and attempts is <= 0,
870
930
  # set events to 1, or the API won't accept it
871
- unless evt == RETRY_EVENTS[:none]
872
- @retry_attempts = 1 unless @retry_attempts.positive?
873
- end
931
+ @retry_attempts = 1 if !(evt == RETRY_EVENTS[:none]) && !@retry_attempts.positive?
874
932
 
875
933
  @retry_event = evt
876
934
  @need_to_update = true
@@ -934,6 +992,7 @@ module Jamf
934
992
  #
935
993
  def target_drive=(path_to_drive)
936
994
  raise Jamf::InvalidDataError, 'Path to target drive must be absolute' unless path_to_drive.to_s.start_with? '/'
995
+
937
996
  @target_drive = path_to_drive.to_s
938
997
  @need_to_update = true
939
998
  end
@@ -946,6 +1005,7 @@ module Jamf
946
1005
  #
947
1006
  def offline=(new_val)
948
1007
  raise Jamf::InvalidDataError, 'New value must be boolean true or false' unless Jamf::TRUE_FALSE.include? new_val
1008
+
949
1009
  @offline = new_val
950
1010
  @need_to_update = true
951
1011
  end
@@ -960,6 +1020,7 @@ module Jamf
960
1020
  #
961
1021
  def set_trigger_event(type, new_val)
962
1022
  raise Jamf::InvalidDataError, "Trigger type must be one of #{TRIGGER_EVENTS.keys.join(', ')}" unless TRIGGER_EVENTS.key?(type)
1023
+
963
1024
  if type == :custom
964
1025
  raise Jamf::InvalidDataError, 'Custom triggers must be Strings' unless new_val.is_a? String
965
1026
  else
@@ -977,6 +1038,7 @@ module Jamf
977
1038
  #
978
1039
  def server_side_activation=(activation)
979
1040
  raise Jamf::InvalidDataError, 'Activation must be a Time' unless activation.is_a? Time
1041
+
980
1042
  @server_side_limitations[:activation] = activation
981
1043
  @need_to_update = true
982
1044
  end
@@ -989,6 +1051,7 @@ module Jamf
989
1051
  #
990
1052
  def server_side_expiration=(expiration)
991
1053
  raise Jamf::InvalidDataError, 'Expiration must be a Time' unless expiration.is_a? Time
1054
+
992
1055
  @server_side_limitations[:expiration] = expiration
993
1056
  @need_to_update = true
994
1057
  end
@@ -999,6 +1062,7 @@ module Jamf
999
1062
  #
1000
1063
  def verify_startup_disk=(bool)
1001
1064
  return if @verify_startup_disk == bool
1065
+
1002
1066
  @verify_startup_disk = Jamf::Validate.boolean bool
1003
1067
  @need_to_update = true
1004
1068
  end
@@ -1007,6 +1071,7 @@ module Jamf
1007
1071
  #
1008
1072
  def permissions_repair=(bool)
1009
1073
  return if @permissions_repair == bool
1074
+
1010
1075
  @permissions_repair = Jamf::Validate.boolean bool
1011
1076
  @need_to_update = true
1012
1077
  end
@@ -1015,6 +1080,7 @@ module Jamf
1015
1080
  #
1016
1081
  def recon=(bool)
1017
1082
  return if @recon == bool
1083
+
1018
1084
  @recon = Jamf::Validate.boolean bool
1019
1085
  @need_to_update = true
1020
1086
  end
@@ -1024,6 +1090,7 @@ module Jamf
1024
1090
  #
1025
1091
  def fix_byhost=(bool)
1026
1092
  return if @fix_byhost == bool
1093
+
1027
1094
  @fix_byhost = Jamf::Validate.boolean bool
1028
1095
  @need_to_update = true
1029
1096
  end
@@ -1032,6 +1099,7 @@ module Jamf
1032
1099
  #
1033
1100
  def reset_name=(bool)
1034
1101
  return if @reset_name == bool
1102
+
1035
1103
  @reset_name = Jamf::Validate.boolean bool
1036
1104
  @need_to_update = true
1037
1105
  end
@@ -1040,6 +1108,7 @@ module Jamf
1040
1108
  #
1041
1109
  def flush_system_cache=(bool)
1042
1110
  return if @flush_system_cache == bool
1111
+
1043
1112
  @flush_system_cache = Jamf::Validate.boolean bool
1044
1113
  @need_to_update = true
1045
1114
  end # see attr_reader :recon
@@ -1048,6 +1117,7 @@ module Jamf
1048
1117
  #
1049
1118
  def install_cached_pkgs=(bool)
1050
1119
  return if @install_cached_pkgs == bool
1120
+
1051
1121
  @install_cached_pkgs = Jamf::Validate.boolean bool
1052
1122
  @need_to_update = true
1053
1123
  end
@@ -1056,6 +1126,7 @@ module Jamf
1056
1126
  #
1057
1127
  def flush_user_cache=(bool)
1058
1128
  return if @flush_user_cache == bool
1129
+
1059
1130
  @flush_user_cache = Jamf::Validate.boolean bool
1060
1131
  @need_to_update = true
1061
1132
  end
@@ -1071,6 +1142,7 @@ module Jamf
1071
1142
  #
1072
1143
  def no_user_logged_in=(no_user_option)
1073
1144
  raise Jamf::InvalidDataError, "no_user_logged_in options: #{NO_USER_LOGGED_IN.join(', ')}" unless NO_USER_LOGGED_IN.include? no_user_option
1145
+
1074
1146
  @reboot_options[:no_user_logged_in] = no_user_option
1075
1147
  @need_to_update = true
1076
1148
  end
@@ -1083,6 +1155,7 @@ module Jamf
1083
1155
  #
1084
1156
  def user_logged_in=(logged_in_option)
1085
1157
  raise Jamf::InvalidDataError, "user_logged_in options: #{USER_LOGGED_IN.join(', ')}" unless USER_LOGGED_IN.include? logged_in_option
1158
+
1086
1159
  @reboot_options[:user_logged_in] = logged_in_option
1087
1160
  @need_to_update = true
1088
1161
  end
@@ -1095,6 +1168,7 @@ module Jamf
1095
1168
  #
1096
1169
  def reboot_message=(message)
1097
1170
  raise Jamf::InvalidDataError, 'Reboot message must be a String' unless message.is_a? String
1171
+
1098
1172
  @reboot_options[:message] = message
1099
1173
  @need_to_update = true
1100
1174
  end
@@ -1107,6 +1181,7 @@ module Jamf
1107
1181
  # @return [void] description of returned object
1108
1182
  def user_message_start=(message)
1109
1183
  raise Jamf::InvalidDataError, 'User message must be a String' unless message.is_a? String
1184
+
1110
1185
  @user_message_start = message
1111
1186
  @need_to_update = true
1112
1187
  end
@@ -1118,6 +1193,7 @@ module Jamf
1118
1193
  # @return [void] description of returned object
1119
1194
  def user_message_end=(message)
1120
1195
  raise Jamf::InvalidDataError, 'User message must be a String' unless message.is_a? String
1196
+
1121
1197
  @user_message_finish = message
1122
1198
  @need_to_update = true
1123
1199
  end
@@ -1133,6 +1209,7 @@ module Jamf
1133
1209
  #
1134
1210
  def startup_disk=(startup_disk_option)
1135
1211
  raise Jamf::InvalidDataError, "#{startup_disk_option} is not a valid Startup Disk" unless startup_disk_option.is_a? String
1212
+
1136
1213
  @reboot_options[:startup_disk] = 'Specify Local Startup Disk'
1137
1214
  self.specify_startup = startup_disk_option
1138
1215
  @need_to_update = true
@@ -1147,6 +1224,7 @@ module Jamf
1147
1224
  #
1148
1225
  def specify_startup=(startup_volume)
1149
1226
  raise Jamf::InvalidDataError, "#{startup_volume} is not a valid Startup Disk" unless startup_volume.is_a? String
1227
+
1150
1228
  @reboot_options[:specify_startup] = startup_volume
1151
1229
  @need_to_update = true
1152
1230
  end
@@ -1172,6 +1250,7 @@ module Jamf
1172
1250
  #
1173
1251
  def minutes_until_reboot=(minutes)
1174
1252
  raise Jamf::InvalidDataError, 'Minutes until reboot must be an Integer' unless minutes.is_a? Integer
1253
+
1175
1254
  @reboot_options[:minutes_until_reboot] = minutes
1176
1255
  @need_to_update = true
1177
1256
  end
@@ -1185,6 +1264,7 @@ module Jamf
1185
1264
  #
1186
1265
  def file_vault_2_reboot=(fv_bool)
1187
1266
  raise Jamf::InvalidDataError, 'FileVault 2 Reboot must be a Boolean' unless fv_bool.jss_boolean?
1267
+
1188
1268
  @reboot_options[:file_vault_2_reboot] = fv_bool
1189
1269
  @need_to_update = true
1190
1270
  end
@@ -1206,6 +1286,7 @@ module Jamf
1206
1286
  #
1207
1287
  def run_command=(command)
1208
1288
  raise Jamf::InvalidDataError, 'Command to run must be a String' unless command.is_a? String
1289
+
1209
1290
  @files_processes[:run_command] = command
1210
1291
  @need_to_update = true
1211
1292
  end
@@ -1262,7 +1343,7 @@ module Jamf
1262
1343
  #
1263
1344
  def search_by_path
1264
1345
  if @files_processes[:search_by_path].nil?
1265
- return nil
1346
+ nil
1266
1347
  else
1267
1348
  Pathname.new @files_processes[:search_by_path]
1268
1349
  end
@@ -1289,6 +1370,7 @@ module Jamf
1289
1370
  #
1290
1371
  def set_search_by_path(path, delete = false)
1291
1372
  raise Jamf::InvalidDataError, 'Path to search for must be a String or a Pathname' unless path.is_a?(String) || path.is_a?(Pathname)
1373
+
1292
1374
  @files_processes[:search_by_path] = path.to_s
1293
1375
  @files_processes[:delete_file] = delete ? true : false
1294
1376
  @need_to_update = true
@@ -1308,6 +1390,7 @@ module Jamf
1308
1390
  #
1309
1391
  def spotlight_search=(term)
1310
1392
  raise Jamf::InvalidDataError, 'Spotlight search term must be a String' unless term.is_a? String
1393
+
1311
1394
  @files_processes[:spotlight_search] = term
1312
1395
  @need_to_update = true
1313
1396
  end
@@ -1326,6 +1409,7 @@ module Jamf
1326
1409
  #
1327
1410
  def locate_file=(term)
1328
1411
  raise Jamf::InvalidDataError, 'Term to locate must be a String' unless term.is_a? String
1412
+
1329
1413
  @files_processes[:locate_file] = term
1330
1414
  @need_to_update = true
1331
1415
  end
@@ -1571,7 +1655,6 @@ module Jamf
1571
1655
  @directory_bindings
1572
1656
  end
1573
1657
 
1574
-
1575
1658
  # Remove a directory binding from this policy by name or id
1576
1659
  #
1577
1660
  # @param identifier [String,Integer] the name or id of the directory binding to remove
@@ -1596,7 +1679,6 @@ module Jamf
1596
1679
  @dock_items.map { |p| p[:name] }
1597
1680
  end
1598
1681
 
1599
-
1600
1682
  ###### Printers
1601
1683
 
1602
1684
  # Add a specific printer object to the policy.
@@ -1661,7 +1743,7 @@ module Jamf
1661
1743
 
1662
1744
  name = Jamf::DockItem.map_all_ids_to(:name, cnx: @cnx)[id]
1663
1745
 
1664
- @dock_items << {id: id, name: name, action: DOCK_ITEM_ACTIONS[action]}
1746
+ @dock_items << { id: id, name: name, action: DOCK_ITEM_ACTIONS[action] }
1665
1747
 
1666
1748
  @need_to_update = true
1667
1749
  @dock_items
@@ -1677,24 +1759,18 @@ module Jamf
1677
1759
 
1678
1760
  # @return [Array] the id's of the printers handled by the policy
1679
1761
  def printer_ids
1680
- begin
1681
- @printers.map { |p| p[:id] }
1682
- rescue TypeError
1683
- return []
1684
- end
1762
+ @printers.map { |p| p[:id] }
1763
+ rescue TypeError
1764
+ []
1685
1765
  end
1686
1766
 
1687
1767
  # @return [Array] the names of the printers handled by the policy
1688
1768
  def printer_names
1689
- begin
1690
- @printers.map { |p| p[:name] }
1691
- rescue TypeError
1692
- return []
1693
- end
1769
+ @printers.map { |p| p[:name] }
1770
+ rescue TypeError
1771
+ []
1694
1772
  end
1695
1773
 
1696
-
1697
-
1698
1774
  ###### Disk Encryption
1699
1775
 
1700
1776
  # Sets the Disk Encryption application to "Remediate" and sets the remediation key type to individual.
@@ -1703,12 +1779,12 @@ module Jamf
1703
1779
  #
1704
1780
  # @return [Void]
1705
1781
  #
1706
- def reissue_key()
1782
+ def reissue_key
1707
1783
  if @disk_encryption[:action] != DISK_ENCRYPTION_ACTIONS[:remediate]
1708
1784
  # Setting New Action
1709
1785
  hash = {
1710
1786
  action: DISK_ENCRYPTION_ACTIONS[:remediate],
1711
- remediate_key_type: "Individual"
1787
+ remediate_key_type: 'Individual'
1712
1788
  }
1713
1789
 
1714
1790
  @disk_encryption = hash
@@ -1716,12 +1792,10 @@ module Jamf
1716
1792
 
1717
1793
  else
1718
1794
  # Update
1719
- return
1795
+ nil
1720
1796
  end
1721
-
1722
1797
  end
1723
1798
 
1724
-
1725
1799
  # Sets the Disk Encryption application to "Apply" and sets the correct disk encryption configuration ID using either the name or id.
1726
1800
  #
1727
1801
  # @author Tyler Morgan
@@ -1729,7 +1803,6 @@ module Jamf
1729
1803
  # @return [Void]
1730
1804
  #
1731
1805
  def apply_encryption_configuration(identifier)
1732
-
1733
1806
  id = Jamf::DiskEncryptionConfiguration.valid_id identifier
1734
1807
 
1735
1808
  return if id.nil?
@@ -1744,14 +1817,13 @@ module Jamf
1744
1817
  @need_to_update = true
1745
1818
  end
1746
1819
 
1747
-
1748
1820
  # Removes the Disk Encryption settings associated with this specific policy.
1749
1821
  #
1750
1822
  # @author Tyler Morgan
1751
1823
  #
1752
1824
  # @return [Void]
1753
1825
  #
1754
- def remove_encryption_configuration()
1826
+ def remove_encryption_configuration
1755
1827
  hash = {
1756
1828
  action: DISK_ENCRYPTION_ACTIONS[:none]
1757
1829
  }
@@ -1774,16 +1846,16 @@ module Jamf
1774
1846
 
1775
1847
  management_data = {}
1776
1848
 
1777
- if action == :change_pw || action == :reset_pw
1778
- raise Jamf::MissingDataError, ":password must be provided when changing management account password" if opts[:password].nil?
1849
+ if %i[change_pw reset_pw].include?(action)
1850
+ raise Jamf::MissingDataError, ':password must be provided when changing management account password' if opts[:password].nil?
1779
1851
 
1780
1852
  management_data = {
1781
1853
  action: MGMT_ACCOUNT_ACTIONS[action],
1782
1854
  managed_password: opts[:password]
1783
1855
  }
1784
- elsif action == :reset_random || action == :generate_pw
1785
- raise Jamf::MissingDataError, ":password_length must be provided when setting a random password" if opts[:password_length].nil?
1786
- raise Jamf::InvalidDataError, ":password_length must be an Integer" unless opts[:password_length].is_a? Integer
1856
+ elsif %i[reset_random generate_pw].include?(action)
1857
+ raise Jamf::MissingDataError, ':password_length must be provided when setting a random password' if opts[:password_length].nil?
1858
+ raise Jamf::InvalidDataError, ':password_length must be an Integer' unless opts[:password_length].is_a? Integer
1787
1859
 
1788
1860
  management_data = {
1789
1861
  action: MGMT_ACCOUNT_ACTIONS[action],
@@ -1800,21 +1872,23 @@ module Jamf
1800
1872
  @need_to_update = true
1801
1873
 
1802
1874
  @management_account
1803
-
1804
1875
  end
1805
1876
 
1806
- # Check if management password matches provided password
1877
+ # @deprecated The API no longer sends SHA256 hashed password data, and instead
1878
+ # only has a string of asterisks, meaning we can no longer use it to validate
1879
+ # passwords before attempting to use them. Instead, the processes that use
1880
+ # the password will fail on their own if the pw is not valid.
1807
1881
  #
1808
- # @param password[String] the password that is SHA256'ed to compare to the one from the API.
1882
+ # This method remains defined for backward-compatibility with any existing
1883
+ # code that calls it. but it will always return true. Itwill be removed in
1884
+ # a future version
1809
1885
  #
1810
- # @return [Boolean] The result of the comparison of the management password and provided text.
1886
+ # @param password[String] ignored
1811
1887
  #
1812
- def verify_management_password(password)
1813
- raise Jamf::InvalidDataError, "Management password must be a string." unless password.is_a? String
1814
-
1815
- raise Jamf::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]
1816
-
1817
- return Digest::SHA256.hexdigest(password).to_s == @management_account[:managed_password_sha256].to_s
1888
+ # @return [TrueClass] Allow the process calling this to continue.
1889
+ #
1890
+ def verify_management_password(_password = nil)
1891
+ true
1818
1892
  end
1819
1893
 
1820
1894
  ###### Actions
@@ -1829,17 +1903,22 @@ module Jamf
1829
1903
  #
1830
1904
  def run(show_output = false)
1831
1905
  return nil unless enabled?
1906
+
1832
1907
  output = Jamf::Client.run_jamf('policy', "-id #{id}", show_output)
1833
1908
  return nil if output.include? 'No policies were found for the ID'
1909
+
1834
1910
  $CHILD_STATUS.exitstatus.zero? ? true : false
1835
1911
  end
1836
1912
  alias execute run
1837
1913
 
1838
- # Flush logs for this policy older than
1839
- # some number of days, weeks, months or years, possibly limited to
1840
- # one or more computers
1914
+ # Flush logs for this policy older than a given time period.
1915
+ #
1916
+ # IMPORTANT: from the Jamf Developer Site:
1917
+ # The ability to flush logs is currently only supported for flushing all logs
1918
+ # for a given policy or all logs for a given computer. There is no support for
1919
+ # flushing logs for a given policy and computer combination.
1841
1920
  #
1842
- # With no parameters, flushes all logs for all computers
1921
+ # With no parameters, will flush all logs for the policy
1843
1922
  #
1844
1923
  # NOTE: Currently the API doesn't have a way to flush only failed policies.
1845
1924
  #
@@ -1851,19 +1930,16 @@ module Jamf
1851
1930
  #
1852
1931
  # @param period[Symbol] :days, :weeks, :months, or :years
1853
1932
  #
1854
- # @param computers[Array<Integer,String>] Identifiers of the target computers
1855
- # either ids, names, SNs, macaddrs, or UDIDs
1856
- #
1857
1933
  # @return [void]
1858
1934
  #
1859
- def flush_logs(older_than: 0, period: :days, computers: [])
1860
- raise Jamf::NoSuchItemError, "Policy doesn't exist in the JSS. Use #create first." unless @in_jss
1935
+ def flush_logs(older_than: 0, period: :days)
1936
+ raise Jamf::NoSuchItemError, "Policy doesn't exist in the JSS. Use #save first." unless @in_jss
1861
1937
 
1862
1938
  Jamf::Policy.flush_logs(
1863
1939
  @id,
1864
1940
  older_than: older_than,
1865
1941
  period: period,
1866
- computers: computers, cnx: @cnx
1942
+ cnx: @cnx
1867
1943
  )
1868
1944
  end
1869
1945
 
@@ -1944,6 +2020,7 @@ module Jamf
1944
2020
 
1945
2021
  id = Jamf::Script.valid_id identifier, cnx: @cnx
1946
2022
  raise Jamf::NoSuchItemError, "No script matches '#{identifier}'" unless id
2023
+
1947
2024
  id
1948
2025
  end
1949
2026
 
@@ -1963,12 +2040,13 @@ module Jamf
1963
2040
  else Jamf::Validate.integer(opts[:position])
1964
2041
  end
1965
2042
 
1966
- # if the given position is past the end, set it to -1 (the end)
1967
- opts[:position] = -1 if opts[:position] > @directory_bindings.size
2043
+ # if the given position is past the end, set it to -1 (the end)
2044
+ opts[:position] = -1 if opts[:position] > @directory_bindings.size
2045
+
2046
+ id = Jamf::DirectoryBinding.valid_id identifier, cnx: @cnx
2047
+ raise Jamf::NoSuchItemError, "No directory binding matches '#{identifier}'" unless id
1968
2048
 
1969
- id = Jamf::DirectoryBinding.valid_id identifier, cnx: @cnx
1970
- raise Jamf::NoSuchItemError, "No directory binding matches '#{identifier}'" unless id
1971
- id
2049
+ id
1972
2050
  end
1973
2051
 
1974
2052
  # Raises an error if the printer being added isn't valid, additionally checks the options and sets defaults where possible.
@@ -1994,14 +2072,17 @@ module Jamf
1994
2072
  raise Jamf::MissingDataError, "action must be provided, must be one of :#{PRINTER_ACTIONS.keys.join(':,')}." if opts[:action].nil?
1995
2073
  raise Jamf::InvalidDataError, "action must be one of :#{PRINTER_ACTIONS.keys.join(',:')}." unless PRINTER_ACTIONS.keys.include? opts[:action]
1996
2074
 
1997
-
1998
2075
  # Checks if the make_default option is valid, and sets the default if needed.
1999
- raise Jamf::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?
2076
+ unless opts[:make_default].is_a?(TrueClass) || opts[:make_default].is_a?(FalseClass) || opts[:make_default].nil?
2077
+ raise Jamf::InvalidDataError,
2078
+ 'make_default must be either true or false.'
2079
+ end
2000
2080
 
2001
2081
  opts[:make_default] = false if opts[:make_default].nil?
2002
2082
 
2003
2083
  id = Jamf::Printer.valid_id identifier, cnx: @cnx
2004
2084
  raise Jamf::NoSuchItemError, "No printer matches '#{identifier}'" unless id
2085
+
2005
2086
  id
2006
2087
  end
2007
2088
 
@@ -2081,7 +2162,7 @@ module Jamf
2081
2162
 
2082
2163
  disk_encryption = obj.add_element 'disk_encryption'
2083
2164
 
2084
- @disk_encryption.each do |k,v|
2165
+ @disk_encryption.each do |k, v|
2085
2166
  disk_encryption.add_element(k.to_s).text = v.to_s
2086
2167
  end
2087
2168
 
@@ -476,7 +476,10 @@ module Jamf
476
476
  item_id = validate_item(:target, key, item)
477
477
  return if @targets[key]&.include?(item_id)
478
478
 
479
- raise Jamf::AlreadyExistsError, "Can't set #{key} target to '#{item}' because it's already an explicit exclusion." if @exclusions[key]&.include?(item_id)
479
+ if @exclusions[key]&.include?(item_id)
480
+ raise Jamf::AlreadyExistsError,
481
+ "Can't set #{key} target to '#{item}' because it's already an explicit exclusion."
482
+ end
480
483
 
481
484
  @targets[key] << item_id
482
485
  @all_targets = false
@@ -775,7 +778,7 @@ module Jamf
775
778
  ################
776
779
  def scoped_machines
777
780
  scoped_machines = {}
778
- @target_class.all_objects(cnx: container.cnx).each do |machine|
781
+ @target_class.all_objects(:refresh, cnx: container.cnx).each do |machine|
779
782
  scoped_machines[machine.id] = machine.name if in_scope? machine
780
783
  end
781
784
  scoped_machines
@@ -19,14 +19,14 @@
19
19
  # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20
20
  # KIND, either express or implied. See the Apache License for the specific
21
21
  # language governing permissions and limitations under the Apache License.
22
- #
23
- #
22
+
23
+ # frozen_string_literal: true
24
24
 
25
25
  module Jamf
26
26
 
27
27
  # This module should be mixed in to Jamf::Computer and Jamf::ComputerGroup
28
28
  #
29
- # It provides access to the macos-managed-software-updates JPAPI resource for
29
+ # It provides access to the macos-managed-software-updates JPAPI resource for
30
30
  # managed OS update commands to managed macs running Big Sur or higher.
31
31
  #
32
32
  module MacOSManagedUpdates
@@ -37,7 +37,7 @@ module Jamf
37
37
  includer.extend(ClassMethods)
38
38
  end
39
39
 
40
- # These resources in the Jamf Pro API can be used to send Managed macOS
40
+ # These resources in the Jamf Pro API can be used to send Managed macOS
41
41
  # updates to clients running Big Sur or higher
42
42
  MANAGED_SW_UPDATES_RSRC = 'v1/macos-managed-software-updates'
43
43
 
@@ -45,7 +45,7 @@ module Jamf
45
45
  MANAGED_SW_UPDATES_AVAILABLE_VERSIONS_RSRC = "#{MANAGED_SW_UPDATES_RSRC}/available-updates"
46
46
 
47
47
  # POSTing JSON data to this resource will send the MDM commands to install os updates
48
- # For details about the data to send, see
48
+ # For details about the data to send, see
49
49
  # https://developer.jamf.com/jamf-pro/reference/post_v1-macos-managed-software-updates-send-updates
50
50
  MANAGED_SW_UPDATES_SEND_UPDATES_RSRC = "#{MANAGED_SW_UPDATES_RSRC}/send-updates"
51
51
 
@@ -53,6 +53,12 @@ module Jamf
53
53
  DOWNLOAD_AND_INSTALL = 'DOWNLOAD_AND_INSTALL'
54
54
  DOWNLOAD_ONLY = 'DOWNLOAD_ONLY'
55
55
 
56
+ # for easier use of these values as the updateAction
57
+ UPDATE_ACTIONS = {
58
+ install: DOWNLOAD_AND_INSTALL,
59
+ download: DOWNLOAD_ONLY
60
+ }
61
+
56
62
  # Class Methods
57
63
  #####################################
58
64
  module ClassMethods
@@ -73,48 +79,52 @@ module Jamf
73
79
 
74
80
  # Send the os update command to target Computers or a ComputerGroup
75
81
  #
76
- # @param updateAction [String] Required. 'DOWNLOAD_AND_INSTALL' or 'DOWNLOAD_ONLY'
82
+ # @param updateAction [Symbol] Required. Use :install to send the
83
+ # DOWNLOAD_AND_INSTALL action, or :download to send DOWNLOAD_ONLY
77
84
  #
78
- # @param deviceIds [String, Integer, Array<String, Integer>] Identifiers for the
85
+ # @param deviceIds [String, Integer, Array<String, Integer>] Identifiers for the
79
86
  # computer targets. Required if no groupId is given.
80
87
  #
81
88
  # @param groupId [String, Integer] Identifier for the computer group target.
82
- # Requied if no deviceIDs are given.
89
+ # Requied if no deviceIds are given.
83
90
  #
84
- # @param maxDeferrals [Integer] Allow users to defer the update the provided number
85
- # of times before macOS forces the update. If a value is provided, the Software
86
- # Update will use the InstallLater install action. MaxDeferral is ignored if using the
87
- # DOWNLOAD_ONLY updateAction.
91
+ # @param maxDeferrals [Integer] Allow users to defer the update the provided number
92
+ # of times before macOS forces the update. If a value is provided, the Software
93
+ # Update will use the InstallLater install action. MaxDeferral is ignored if using the
94
+ # :download updateAction.
88
95
  #
89
- # @param version [String] The OS version to install. If no value is provided, the
96
+ # @param version [String] The OS version to install. If no value is provided, the
90
97
  # version will default to latest version based on device eligibility.
91
98
  #
92
99
  # @param skipVersionVerification [Boolean] Should the specified version be installed
93
100
  # even it it isn't applicable to this machine? If no value is provided, will default to false.
94
- # If true, the specified version will be forced to complete DownloadAndInstall
95
- # install action.
101
+ # If true, the specified version will be forced to complete the :install updateAction.
96
102
  #
97
103
  # @param applyMajorUpdate [Boolean] Available only when updating to the latest version
98
104
  # based on device eligibility. Defaults to false. If false the calculated latest version
99
105
  # will only include minor version updates. If a value is provided, the calculated latest
100
106
  # version will include minor and major version updates.
101
107
  #
102
- # @param forceRestart [Boolean] Will default to false. Can only be true if updateAction
103
- # is DOWNLOAD_AND_INSTALLn and the devices the command is sent to are on macOs 11 or higher.
104
- # If true, the DownloadAndInstall action is performed, a restart will be forced.
105
- # MaxDeferral will be ignored if true.
108
+ # @param forceRestart [Boolean] Will default to false. Can only be true if updateAction
109
+ # is :install and the target devices are on macOs 11 or higher.
110
+ # If true, the DownloadAndInstall action is performed, a restart will be forced.
111
+ # MaxDeferral will be ignored if true.
106
112
  #
107
- # @param cnx [Jamf::Connection] The API connection to use. Defaults to Jamf.cnx
113
+ # @param cnx [Jamf::Connection] The API connection to use. Defaults to Jamf.cnx
108
114
  #
109
- # @return [Jamf::OAPISchemas::MacOsManagedSoftwareUpdateResponse]
115
+ # @return [Jamf::OAPISchemas::MacOsManagedSoftwareUpdateResponse]
110
116
  ########################
111
117
  def send_managed_os_update(updateAction:, deviceIds: nil, groupId: nil, maxDeferrals: nil, version: nil, skipVersionVerification: false, applyMajorUpdate: false, forceRestart: false, cnx: Jamf.cnx)
112
- if self == Jamf::Computer
118
+ action_to_send = UPDATE_ACTIONS.value?(updateAction) ? updateAction : UPDATE_ACTIONS[updateAction]
119
+
120
+ raise ArgumentError, "Unknown updateAction, must be one of: #{UPDATE_ACTIONS.keys.join ', '}" unless action_to_send
121
+
122
+ if self == Jamf::Computer
113
123
  raise ArgumentError, 'Must provide one or more deviceIds' unless deviceIds
114
124
  elsif self == Jamf::ComputerGroup
115
125
  raise ArgumentError, 'Must provide a groupId' unless groupId
116
126
  else
117
- raise Jamf::UnsupportedError, 'This method is only available for Jamf::Computer and Jamf::ComputerGroup'
127
+ raise Jamf::UnsupportedError, 'This method is only available for Jamf::Computer and Jamf::ComputerGroup'
118
128
  end
119
129
 
120
130
  if version
@@ -124,9 +134,9 @@ module Jamf
124
134
 
125
135
  if deviceIds
126
136
  deviceIds = [deviceIds] unless deviceIds.is_a?(Array)
127
- deviceIds.map! { |id| valid_id id }
137
+ deviceIds.map! { |id| valid_id id, cnx: cnx }
128
138
  end
129
- groupId = valid_id groupId if groupId
139
+ groupId = valid_id(groupId, cnx: cnx) if groupId
130
140
 
131
141
  data = {}
132
142
  # ids in the JPAPI are string containing integers
@@ -137,11 +147,11 @@ module Jamf
137
147
  data[:version] = version if version
138
148
  data[:skipVersionVerification] = skipVersionVerification if skipVersionVerification
139
149
  data[:applyMajorUpdate] = applyMajorUpdate if applyMajorUpdate
140
- data[:updateAction] = updateAction if updateAction
141
- data[:forceRestart] = forceRestart if forceRestart
142
-
150
+ data[:updateAction] = action_to_send
151
+ data[:forceRestart] = forceRestart if forceRestart
152
+
143
153
  payload = Jamf::OAPISchemas::MacOsManagedSoftwareUpdate.new(data).to_json
144
-
154
+
145
155
  result = cnx.jp_post MANAGED_SW_UPDATES_SEND_UPDATES_RSRC, payload
146
156
  Jamf::OAPISchemas::MacOsManagedSoftwareUpdateResponse.new result
147
157
  end
@@ -161,14 +171,14 @@ module Jamf
161
171
  groupId = is_a?(Jamf::Computer) ? nil : @id
162
172
 
163
173
  self.class.send_managed_os_update(
164
- deviceIds: deviceIds,
165
- groupId: groupId,
166
- maxDeferrals: maxDeferrals,
167
- version: version,
168
- skipVersionVerification: skipVersionVerification,
169
- applyMajorUpdate: applyMajorUpdate,
170
- forceRestart: forceRestart,
171
- updateAction: updateAction,
174
+ deviceIds: deviceIds,
175
+ groupId: groupId,
176
+ maxDeferrals: maxDeferrals,
177
+ version: version,
178
+ skipVersionVerification: skipVersionVerification,
179
+ applyMajorUpdate: applyMajorUpdate,
180
+ forceRestart: forceRestart,
181
+ updateAction: updateAction,
172
182
  cnx: @cnx
173
183
  )
174
184
  end
data/lib/jamf/validate.rb CHANGED
@@ -98,7 +98,9 @@ module Jamf
98
98
  #
99
99
  # @return [Object] the validated unique value
100
100
  #
101
- def self.doesnt_already_exist(klass, identifier, val, msg: nil, api: Jamf.cnx)
101
+ def self.doesnt_already_exist(klass, identifier, val, msg: nil, api: nil, cnx: Jamf.cnx)
102
+ cnx = api if api
103
+
102
104
  return val unless klass.all(:refresh, cnx: cnx).map { |i| i[identifier] }.include? val
103
105
 
104
106
  key = klass.real_lookup_key identifier
data/lib/jamf/version.rb CHANGED
@@ -27,6 +27,6 @@
27
27
  module Jamf
28
28
 
29
29
  ### The version of ruby-jss
30
- VERSION = '2.1.0'.freeze
30
+ VERSION = '3.0.0b1'.freeze
31
31
 
32
32
  end # module
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-jss
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0b1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Lasell
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2022-10-11 00:00:00.000000000 Z
13
+ date: 2023-04-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: CFPropertyList
@@ -824,9 +824,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
824
824
  version: 2.6.3
825
825
  required_rubygems_version: !ruby/object:Gem::Requirement
826
826
  requirements:
827
- - - ">="
827
+ - - ">"
828
828
  - !ruby/object:Gem::Version
829
- version: '0'
829
+ version: 1.3.1
830
830
  requirements: []
831
831
  rubygems_version: 3.0.3.1
832
832
  signing_key: