ruby-jss 0.14.0 → 1.0.0b2

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.

Potentially problematic release.


This version of ruby-jss might be problematic. Click here for more details.

@@ -98,6 +98,12 @@ module JSS
98
98
  # Where is the Site data in the API JSON?
99
99
  SITE_SUBSET = :general
100
100
 
101
+ # Where is the Category in the API JSON?
102
+ CATEGORY_SUBSET = :general
103
+
104
+ # How is the category stored in the API data?
105
+ CATEGORY_DATA_TYPE = Hash
106
+
101
107
  # Attributes
102
108
  #####################################
103
109
 
@@ -97,6 +97,13 @@ module JSS
97
97
  # See {APIObject#add_object_history_entry}
98
98
  OBJECT_HISTORY_OBJECT_TYPE = 90
99
99
 
100
+ # Where is the Category in the API JSON?
101
+ CATEGORY_SUBSET = :top
102
+
103
+ # How is the category stored in the API data?
104
+ CATEGORY_DATA_TYPE = String
105
+
106
+
100
107
  ### Class Variables
101
108
  #####################################
102
109
 
@@ -118,7 +125,7 @@ module JSS
118
125
  ### @return [Boolean] does this pkg also get install in the OS user homedir template
119
126
  attr_reader :fill_user_template
120
127
 
121
- ### @return [Boolean] does this item require a reboot after installation? If so, it'll be a puppy-install in d3
128
+ ### @return [Boolean] does this item require a reboot after installation?
122
129
  attr_reader :reboot_required
123
130
 
124
131
  ### @return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
@@ -26,13 +26,599 @@
26
26
  ###
27
27
  module JSS
28
28
 
29
- # This is a stub - Patch Policy data in the API is still borked.
30
- # Waiting for fixes from Jamf.
29
+ # A Patch Policy in the JSS
30
+ #
31
+ # When making new Patch Polices :patch_title and :target_version must be
32
+ # provided as well as :name.
33
+ #
34
+ # :patch_title is the name or id of a currently active patch title
35
+ #
36
+ # :target_version is the string identfier of an available version of
37
+ # the title. The target version MUST have a package assigned to it.
38
+ #
39
+ # See {JSS::PatchTitle} and {JSS::PatchSource.available_titles} for methods
40
+ # to acquire such info.
31
41
  #
32
42
  # @see JSS::APIObject
33
43
  #
34
44
  class PatchPolicy < JSS::APIObject
35
45
 
46
+ include JSS::SelfServable
47
+ include JSS::Scopable
48
+ include JSS::Creatable
49
+ include JSS::Updatable
50
+
51
+ RSRC_BASE = 'patchpolicies'.freeze
52
+
53
+ RSRC_LIST_KEY = :patch_policies
54
+
55
+ RSRC_OBJECT_KEY = :patch_policy
56
+
57
+ RSRC_BY_PATCH_TITLE = 'patchpolicies/softwaretitleconfig/id/'.freeze
58
+
59
+ # TODO: complain to jamf about this - should be the same as RSRC_LIST_KEY
60
+ RSRC_BY_PATCH_TITLE_LIST_KEY = :"patch policies"
61
+
62
+ SCOPE_TARGET_KEY = :computers
63
+
64
+ AUTO_INSTALL_GRACE_PERIOD_MESSAGE = '$APP_NAMES will quit in $DELAY_MINUTES minutes so that $SOFTWARE_TITLE can be updated. Save anything you are working on and quit the app(s).'.freeze
65
+
66
+ DFT_ENABLED = false
67
+
68
+ # the default dist method - not in ssvc
69
+ DFT_DISTRIBUTION = 'prompt'.freeze
70
+
71
+ # the value of #deadline when there is no deadline
72
+ NO_DEADLINE = :none
73
+
74
+ DFT_DEADLINE = 7
75
+
76
+ # The valud of #grace_period when not defined
77
+ DFT_GRACE_PERIOD = 15
78
+
79
+ DFT_GRACE_PERIOD_SUBJECT = 'Important'.freeze
80
+
81
+ DFT_GRACE_PERIOD_MESSAGE = '$APP_NAMES will quit in $DELAY_MINUTES minutes so that $SOFTWARE_TITLE can be updated. Save anything you are working on and quit the app(s).'.freeze
82
+
83
+ # See {JSS::XMLWorkaround}
84
+ USE_XML_WORKAROUND = {
85
+ patch_policy: {
86
+ general: {
87
+ id: -1,
88
+ name: JSS::BLANK,
89
+ enabled: nil,
90
+ target_version: JSS::BLANK,
91
+ release_date: 0,
92
+ incremental_update: nil,
93
+ reboot: nil,
94
+ minimum_os: JSS::BLANK,
95
+ kill_apps: [
96
+ {
97
+ kill_app_name: JSS::BLANK,
98
+ kill_app_bundle_id: JSS::BLANK
99
+ }
100
+ ],
101
+ distribution_method: JSS::BLANK,
102
+ allow_downgrade: nil,
103
+ patch_unknown: nil
104
+ },
105
+ scope: {
106
+ all_computers: nil,
107
+ computers: [
108
+ {
109
+ id: -1,
110
+ name: JSS::BLANK,
111
+ udid: JSS::BLANK
112
+ }
113
+ ],
114
+ computer_groups: [
115
+ {
116
+ id: -1,
117
+ name: JSS::BLANK
118
+ }
119
+ ],
120
+ users: [
121
+ {
122
+ id: -1,
123
+ username: JSS::BLANK
124
+ }
125
+ ],
126
+ user_groups: [
127
+ {
128
+ id: -1,
129
+ name: JSS::BLANK
130
+ }
131
+ ],
132
+ buildings: [
133
+ {
134
+ id: -1,
135
+ name: JSS::BLANK
136
+ }
137
+ ],
138
+ departments: [
139
+ {
140
+ id: -1,
141
+ name: JSS::BLANK
142
+ }
143
+ ],
144
+ limitations: {
145
+ network_segments: [
146
+ {
147
+ id: -1,
148
+ name: JSS::BLANK
149
+ }
150
+ ],
151
+ ibeacons: [
152
+ {
153
+ id: -1,
154
+ name: JSS::BLANK
155
+ }
156
+ ]
157
+ },
158
+ exclusions: {
159
+ computers: [
160
+ {
161
+ id: -1,
162
+ name: JSS::BLANK,
163
+ udid: JSS::BLANK
164
+ }
165
+ ],
166
+ computer_groups: [
167
+ {
168
+ id: -1,
169
+ name: JSS::BLANK
170
+ }
171
+ ],
172
+ users: [
173
+ {
174
+ id: -1,
175
+ username: JSS::BLANK
176
+ }
177
+ ],
178
+ user_groups: [
179
+ {
180
+ id: -1,
181
+ name: JSS::BLANK
182
+ }
183
+ ],
184
+ buildings: [
185
+ {
186
+ id: -1,
187
+ name: JSS::BLANK
188
+ }
189
+ ],
190
+ departments: [
191
+ {
192
+ id: -1,
193
+ name: JSS::BLANK
194
+ }
195
+ ],
196
+ network_segments: [
197
+ {
198
+ id: -1,
199
+ name: JSS::BLANK
200
+ }
201
+ ],
202
+ ibeacons: [
203
+ {
204
+ id: -1,
205
+ name: JSS::BLANK
206
+ }
207
+ ]
208
+ }
209
+ },
210
+ user_interaction: {
211
+ install_button_text: JSS::BLANK,
212
+ self_service_description: JSS::BLANK,
213
+ self_service_icon: {
214
+ id: -1,
215
+ filename: JSS::BLANK,
216
+ uri: JSS::BLANK
217
+ },
218
+ notifications: {
219
+ notification_enabled: nil,
220
+ notification_type: JSS::BLANK,
221
+ notification_subject: JSS::BLANK,
222
+ notification_message: JSS::BLANK,
223
+ reminders: {
224
+ notification_reminders_enabled: nil,
225
+ notification_reminder_frequency: 1
226
+ }
227
+ },
228
+ deadlines: {
229
+ deadline_enabled: nil,
230
+ deadline_period: 7
231
+ },
232
+ grace_period: {
233
+ grace_period_duration: 15,
234
+ notification_center_subject: 'Important',
235
+ message: AUTO_INSTALL_GRACE_PERIOD_MESSAGE
236
+ }
237
+ },
238
+ software_title_configuration_id: 2
239
+ }
240
+ }.freeze
241
+
242
+ # Class Methods
243
+ ################################
244
+
245
+ # Fetch name and id of all PatchPolicies tied to a given PatchTitle
246
+ #
247
+ # @param title[String,Integer] the name or id of the PatchTitle for which
248
+ # to retrieve a list of patch policies
249
+ #
250
+ # @return [Array<Hash>] the :id and :name of each policy for the title
251
+ #
252
+ def self.all_for_title(title, api: JSS.api)
253
+ title_id = JSS::PatchTitle.valid_id title
254
+ raise JSS::NoSuchItemError, "No PatchTitle matching '#{title}'" unless title_id
255
+ api.get_rsrc("#{RSRC_BY_PATCH_TITLE}#{title_id}")[RSRC_BY_PATCH_TITLE_LIST_KEY]
256
+ end
257
+
258
+ # Attributes
259
+ ################################
260
+
261
+ # @return [Boolean] is this patch policy enabled?
262
+ attr_reader :enabled
263
+ alias enabled? enabled
264
+
265
+ # When setting, the version must exist in the policy's PatchTitle,
266
+ # and have a package assigned to it.
267
+ #
268
+ # @param new_tgt_vers[String] the new version for this Patch Policy.
269
+ #
270
+ # @return [String] The version deployed by this policy
271
+ #
272
+ attr_reader :target_version
273
+ alias version target_version
274
+
275
+ # @return [Time] when the target_version was released
276
+ attr_reader :release_date
277
+
278
+ # @return [Boolean] must this patch be installed only over the prev. version?
279
+ attr_reader :incremental_update
280
+ alias incremental_update? incremental_update
281
+
282
+ # @return [Boolean] does this patch require a reboot after installation?
283
+ attr_reader :reboot
284
+ alias reboot_required reboot
285
+ alias reboot? reboot
286
+ alias reboot_required? reboot
287
+
288
+ # @return [String] The min. OS version require to install this patch
289
+ attr_reader :minimum_os
290
+
291
+ # @return [Array<Hash>] The apps that cannot be running when this is installed.
292
+ # each Hash contains :kill_app_name and :kill_app_bundle_id, both Strings
293
+ attr_reader :kill_apps
294
+
295
+ # Can this title be downgraded to this version?
296
+ # @param new_val [Boolean]
297
+ # @return [Boolean]
298
+ attr_reader :allow_downgrade
299
+ alias allow_downgrade? allow_downgrade
300
+ alias downgradable? allow_downgrade
301
+
302
+ # Can this policy run when we don't know the prev. version?
303
+ # @param new_val [Boolean]
304
+ # @return [Boolean]
305
+ attr_reader :patch_unknown
306
+ alias patch_unknown? patch_unknown
307
+
308
+ # How many days is the install deadline?
309
+ # @param days [Integer, Symbol] :none, or a positive integer. Integers < 1
310
+ # have the same meaning as :none
311
+ #
312
+ # @return [Integer, Symnol] :none, or a positive integer
313
+ attr_reader :deadline
314
+
315
+ # @param new_period [Integer] Negative integers will be saved as 0
316
+ #
317
+ # @return [Integer] How many minutes does the user have to quit the killapps?
318
+ #
319
+ attr_reader :grace_period
320
+ alias grace_period_duration grace_period
321
+
322
+ # @param subj [String] the new subject
323
+ #
324
+ # @return [String] The Subject of the message displayed asking the user to
325
+ # quit the killapps within @grace_period minutes
326
+ attr_reader :grace_period_subject
327
+ alias grace_period_notification_center_subject grace_period_subject
328
+
329
+ # @param subj [String] the new message
330
+ #
331
+ # @return [String] The message displayed asking the user to quit the killapps
332
+ # within @grace_period minutes
333
+ attr_reader :grace_period_message
334
+
335
+ # @return [Integer] the id of the JSS::PatchTitle for this policy.
336
+ # Can be set with the patch_title: param of .make, but is read-only after
337
+ # that.
338
+ attr_reader :patch_title_id
339
+ alias software_title_id patch_title_id
340
+ alias software_title_configuration_id patch_title_id
341
+
342
+ # When making new Patch Polices :patch_title is required and is
343
+ # a JSS::PatchTitle or the name or id of one
344
+ #
345
+ # If target_version: is provided, it must exist in the PatchTitle,
346
+ # and must have a package assigned to it.
347
+ #
348
+ def initialize(data = {})
349
+ super
350
+
351
+ # creation...
352
+ unless in_jss
353
+ @init_data[:general] ||= {}
354
+ @init_data[:software_title_configuration_id] = validate_patch_title @init_data[:patch_title]
355
+
356
+ # were we given target_version in the make params?
357
+ validate_target_version @init_data[:target_version] if @init_data[:target_version]
358
+ @init_data[:general][:target_version] = @init_data[:target_version]
359
+
360
+ # other defaults
361
+ @init_data[:general][:enabled] = false
362
+ @init_data[:general][:allow_downgrade] = false
363
+ @init_data[:general][:patch_unknown] = false
364
+ @init_data[:general][:distribution_method] = DFT_DISTRIBUTION
365
+ end
366
+
367
+ @patch_title_id = @init_data[:software_title_configuration_id]
368
+
369
+ gen = @init_data[:general]
370
+ @enabled = gen[:enabled]
371
+ @target_version = gen[:target_version]
372
+ @allow_downgrade = gen[:allow_downgrade]
373
+ @patch_unknown = gen[:patch_unknown]
374
+
375
+ @init_data[:user_interaction] ||= {}
376
+
377
+ deadlines = @init_data[:user_interaction][:deadlines]
378
+ deadlines ||= {}
379
+ deadlines[:deadline_period] = DFT_DEADLINE if deadlines[:deadline_period].to_s.empty?
380
+ @deadline = deadlines[:deadline_enabled] ? deadlines[:deadline_period] : NO_DEADLINE
381
+
382
+ grace = @init_data[:user_interaction][:grace_period]
383
+ grace ||= {}
384
+
385
+ @grace_period = grace[:grace_period_duration]
386
+ @grace_period = DFT_GRACE_PERIOD if @grace_period.to_s.empty?
387
+
388
+ @grace_period_subject = grace[:notification_center_subject]
389
+ @grace_period_subject = DFT_GRACE_PERIOD_SUBJECT if @grace_period_subject.to_s.empty?
390
+
391
+ @grace_period_message = grace[:message]
392
+ @grace_period_message = DFT_GRACE_PERIOD_MESSAGE if @grace_period_message.to_s.empty?
393
+
394
+
395
+ # read-only values, they come from the version.
396
+ @release_date = JSS.epoch_to_time gen[:release_date]
397
+ @incremental_update = gen[:incremental_update]
398
+ @reboot = gen[:reboot]
399
+ @minimum_os = gen[:minimum_os]
400
+ @kill_apps = gen[:kill_apps]
401
+ end
402
+
403
+ # The JSS::PatchTitle to for this PatchPolicy
404
+ #
405
+ # @param refresh [Boolean] Should the Title be re-fetched from the API?
406
+ #
407
+ # @return [JSS::PatchTitle, nil]
408
+ #
409
+ def patch_title(refresh = false)
410
+ @patch_title = nil if refresh
411
+ @patch_title ||= JSS::PatchTitle.fetch id: patch_title_id
412
+ end
413
+
414
+ # @return [String] the name of the PatchTitle for this patch policy
415
+ #
416
+ def patch_title_name
417
+ return @patch_title.name if @patch_title
418
+ JSS::PatchTitle.map_all_ids_to(:name)[software_title_configuration_id]
419
+ end
420
+
421
+ # See attr_reader :target_version
422
+ #
423
+ def target_version=(new_tgt_vers)
424
+ return if new_tgt_vers == target_version
425
+ @target_version = validate_target_version new_tgt_vers
426
+ @need_to_update = true
427
+ @refetch_for_new_version = true
428
+ end
429
+
430
+ # enable this policy
431
+ #
432
+ # @return [void]
433
+ #
434
+ def enable
435
+ return if enabled
436
+ @enabled = true
437
+ @need_to_update = true
438
+ end
439
+
440
+ # disable this policy
441
+ #
442
+ # @return [void]
443
+ #
444
+ def disable
445
+ return unless enabled
446
+ @enabled = false
447
+ @need_to_update = true
448
+ end
449
+
450
+ # see attr_reader :allow_downgrade
451
+ #
452
+ def allow_downgrade=(new_val)
453
+ return if new_val == allow_downgrade
454
+ @allow_downgrade = JSS::Validate.boolean new_val
455
+ @need_to_update = true
456
+ end
457
+
458
+ # see attr_reader :patch_unknown
459
+ #
460
+ def patch_unknown=(new_val)
461
+ return if new_val == patch_unknown
462
+ @patch_unknown = JSS::Validate.boolean new_val
463
+ @need_to_update = true
464
+ end
465
+
466
+ # see attr_reader :deadline
467
+ #
468
+ def deadline=(days)
469
+ unless days == NO_DEADLINE
470
+ days = JSS::Validate.integer(days)
471
+ days = NO_DEADLINE unless days.positive?
472
+ end
473
+ return if days == deadline
474
+ @deadline = days
475
+ @need_to_update = true
476
+ end
477
+
478
+ # see attr_reader :grace_period
479
+ #
480
+ def grace_period=(mins)
481
+ mins = JSS::Validate.integer(mins)
482
+ mins = 0 if mins.negative?
483
+ return if mins == grace_period
484
+ @grace_period = mins
485
+ @need_to_update = true
486
+ end
487
+
488
+ # see attr_reader :grace_period_subject
489
+ #
490
+ def grace_period_subject=(subj)
491
+ return if grace_period_subject == subj.to_s
492
+ @grace_period_subject = subj.to_s
493
+ @need_to_update = true
494
+ end
495
+
496
+ # see attr_reader :grace_period_message
497
+ #
498
+ def grace_period_message=(msg)
499
+ return if grace_period_message == msg
500
+ @grace_period_message = msg
501
+ @need_to_update = true
502
+ end
503
+
504
+ # Create a new PatchPolicy in the JSS
505
+ #
506
+ # @return [Integer] the id of the new policy
507
+ #
508
+ def create
509
+ validate_for_saving
510
+ # TODO: prepare for more cases where the POST rsrc is
511
+ # different from the PUT/GET/DELETE.
512
+ orig_rsrc = @rest_rsrc
513
+ @rest_rsrc = "#{RSRC_BY_PATCH_TITLE}#{patch_title_id}"
514
+ super
515
+ @rest_rsrc = orig_rsrc
516
+ refetch_version_info
517
+ id
518
+ end
519
+
520
+ # Update an existing PatchPolicy with changes from ruby
521
+ #
522
+ # @return [Integer] the id of the policy
523
+ #
524
+ def update
525
+ validate_for_saving
526
+ super
527
+ refetch_version_info if @refetch_for_new_version
528
+ @refetch_for_new_version = false
529
+ id
530
+ end
531
+
532
+ # Private Instance Methods
533
+ #####################################
534
+ private
535
+
536
+ # raise an error if the patch title we're trying to use isn't available in
537
+ # the jss. If handed a PatchTitle instance, we assume it came from the JSS
538
+ #
539
+ ## @param new_title[String,Integer,JSS::PatchTitle] the title to validate
540
+ #
541
+ # @return [Integer] the id of the valid title
542
+ #
543
+ def validate_patch_title(a_title)
544
+ if a_title.is_a? JSS::PatchTitle
545
+ @patch_title = a_title
546
+ return a_title.id
547
+ end
548
+ raise JSS::MissingDataError, ':patch_title is required' unless a_title
549
+ title_id = JSS::PatchTitle.valid_id a_title
550
+ return title_id if title_id
551
+ raise JSS::NoSuchItemError, "No Patch Title matches '#{a_title}'"
552
+ end
553
+
554
+ # raise an exception if a given target version is not valid for this policy
555
+ # Otherwise return it
556
+ #
557
+ def validate_target_version(tgt_vers)
558
+ raise JSS::MissingDataError, "target_version can't be nil" unless tgt_vers
559
+
560
+ JSS::Validate.non_empty_string tgt_vers
561
+
562
+ unless patch_title(:refresh).versions.key? tgt_vers
563
+ errmsg = "Version '#{tgt_vers}' does not exist for title: #{patch_title_name}."
564
+ raise JSS::NoSuchItemError, errmsg
565
+ end
566
+
567
+ return tgt_vers if patch_title.versions_with_packages.key? tgt_vers
568
+
569
+ errmsg = "Version '#{tgt_vers}' cannot be used in Patch Policies until a package is assigned to it."
570
+ raise JSS::UnsupportedError, errmsg
571
+ end
572
+
573
+ def validate_for_saving
574
+ validate_target_version target_version
575
+ end
576
+
577
+ # Update our local version data after the target_version is changed
578
+ #
579
+ def refetch_version_info
580
+ tmp = self.class.fetch id: id
581
+ @release_date = tmp.release_date
582
+ @incremental_update = tmp.incremental_update
583
+ @reboot = tmp.reboot
584
+ @minimum_os = tmp.minimum_os
585
+ @kill_apps = tmp.kill_apps
586
+ end
587
+
588
+ def rest_xml
589
+ doc = REXML::Document.new JSS::APIConnection::XML_HEADER
590
+ obj = doc.add_element RSRC_OBJECT_KEY.to_s
591
+
592
+ general = obj.add_element 'general'
593
+ general.add_element('target_version').text = target_version
594
+ general.add_element('name').text = name
595
+ general.add_element('enabled').text = enabled?.to_s
596
+ general.add_element('allow_downgrade').text = allow_downgrade
597
+ general.add_element('patch_unknown').text = patch_unknown
598
+
599
+ obj << scope.scope_xml
600
+
601
+ add_self_service_xml doc
602
+
603
+ # self svc xml gave us the user_interaction section
604
+ user_int = obj.elements['user_interaction']
605
+
606
+ dlines = user_int.add_element 'deadlines'
607
+ if deadline == NO_DEADLINE
608
+ dlines.add_element('deadline_enabled').text = 'false'
609
+ else
610
+ dlines.add_element('deadline_enabled').text = 'true'
611
+ dlines.add_element('deadline_period').text = deadline.to_s
612
+ end
613
+
614
+ grace = user_int.add_element 'grace_period'
615
+ grace.add_element('grace_period_duration').text = grace_period.to_s
616
+ grace.add_element('notification_center_subject').text = grace_period_subject.to_s
617
+ grace.add_element('message').text = grace_period_message.to_s
618
+
619
+ doc.to_s
620
+ end
621
+
36
622
  end # class PatchPolicy
37
623
 
38
624
  end # module JSS