ruby-jss 1.5.3 → 1.6.0b1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5149851702056e5500ec5e13624a91c78510fceb3b3fa0545aac69f756adb984
4
- data.tar.gz: 42c5312c401d7b1e626f5c97c9697a0ec9cf5070be310301e307e5b83f8c4cc1
3
+ metadata.gz: db33099b85c8a28905abb4885931b4782f169604fdda26458f0def90d9cec4cb
4
+ data.tar.gz: f004c3aa0aaed1934ccdf98dcce510af7f7c54e399d48692cccc6d9cffef011f
5
5
  SHA512:
6
- metadata.gz: '09b9e201339f62871c2eb74e78cf5d3674997b0d984eb1d537431be60efcbe83a82e383b88873d306d2a89e1a6fc3259c0b1e9fcbda1323caac48198510f6e1a'
7
- data.tar.gz: bb07643c17c47532bb3469853ea0d0eb317e5e97bcbcf8ed07b44d7f9b6e7ce794a4f20154e2f2e4986ba4291576bb52ec0c645a180fb5c98d1b3443b5588317
6
+ metadata.gz: 8ceb6d36bade8f595556f4c6208b318187bb37f228ae183d8204190f82129c3eaeb3580cfc56277d45d16e84d6889c9220c7a6b2dc02c91c689b9246355280f1
7
+ data.tar.gz: afa31ca7c07c083b0ee1c255153cdfc02c423e72bdcb4421035b8debe87a365c232d049932441a1583671a5552d4519a1307c8f18ca8e236114a79e1d0c8f3e4
data/CHANGES.md CHANGED
@@ -4,6 +4,31 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## \[1.6.0] - 2021-05-??
8
+
9
+ ### Fixed
10
+
11
+ - Creating JSS::User's no longer requires a valid LDAP server. Many thanks to @aaron-mmt for filing and fixing this issue!
12
+
13
+ - HTTP 409 errors are handled more appropriately, and should report the actual error message from the server, e.g. 'Duplicate Primary MAC Address'
14
+
15
+ ### Changed
16
+
17
+ - ruby-jss no longer uses the 'plist' gem due to a remote code execution security issue when using `Plist.parse_xml`. Plists are now handled by the CFPropertyList gem. The existing wrapper method `JSS.parse_plist` bas been updated to use the new gem, and a new wrapper method has been added to convert ruby data to XML plist: `JSS.xml_plist_from(data)`. All internal references to methods from the insecure 'plist' gem have been replaced with calls to those wrapper methods.
18
+
19
+ Many many thanks to actae0n of Blacksun Hackers Club for reporting this security issue and providing examples of how it could be exploited.
20
+
21
+ - In preparation for the removal of the 'runScript' command in the jamf binary, JSS::Script no longer uses it within the 'run' instance method. Instead, it just does what the jamf binary did: It creates a private temp folder, writes the script to disk in that temp folder, executes the script with any given params, then deletes the folder, returning the exit status and output from the script.
22
+
23
+ - Jamf::Script#run now takes parameters in the named params `p4:` through `p11:` for consistency with other parts of ruby-jss.
24
+
25
+ - Since Jamf Scripts can no longer be stored in a distribution point, which according to Jamf has been the case for a while, all code for dealing with them that way has been removed.
26
+
27
+ - JSS::NetworkSegment#include? now uses Range#cover? under the hood, rather than Range#include?, which can take a very very long time with large segments.
28
+
29
+ - JSS.expand_min_os has been updated to handle Apple's new version numbers for macOS. This method takes a string like '>=10.14.3' and expands it into a large array of greater OS versions and is used by the 'os_limitations' method of Packages and Scripts. For any range of versions that includes Big Sur, both '11.x.x' and '10.16' as included in the output, to catch machines that may have SYSTEM_VERSION_COMPAT set in their env.
30
+
31
+
7
32
  ## \[1.5.3] - 2020-12-28
8
33
 
9
34
  ### Fixed
data/THANKS.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # With Thanks To...
2
2
 
3
+ * Everyone who contributes to this project
3
4
  * Pixar Systems Management
4
- * The Pixar Mac Team
5
+ * The Apple @ Pixar team
5
6
  * JAMF Folks
6
- * Alex
7
+ * Alex
data/lib/jamf.rb CHANGED
@@ -42,7 +42,6 @@ require 'digest'
42
42
  require 'open3'
43
43
 
44
44
  ### Gems
45
- require 'plist'
46
45
 
47
46
  # Used, among other places, in the Connection::APIError class
48
47
  require 'immutable-struct'
@@ -176,18 +176,18 @@ module Jamf
176
176
  # those parameters will return the cached Array. Use `refresh: true` to
177
177
  # re-request that Array from the server. Note that the cache is of the raw
178
178
  # JSON Hash data. Using 'instantiate:' will still be slower as each item in
179
- # the cache is instantiated. See 'Instantiation' below.
179
+ # the cache is instantiated. See 'Instantiation' above.
180
180
  #
181
- # Some other methods, e.g. .all_names, will generate or use this cached Array
182
- # to derive their values.
181
+ # Some other class methods, e.g. .all_names, will generate or use this cached
182
+ # Array to derive their values.
183
183
  #
184
184
  # If any of the parameters paged:, sort:, or filter: are used, an API
185
185
  # request is made every time, and no caches are used or stored.
186
186
  #
187
187
  #######
188
188
  #
189
- # @param sort [String, Array<String>] Server-side sorting criteria in the format:
190
- # property:direction, where direction is 'asc' or 'desc'. Multiple
189
+ # @param sort [String, Array<String>] Server-side sorting criteria in the
190
+ # format: property:direction, where direction is 'asc' or 'desc'. Multiple
191
191
  # properties are supported, either as separate strings in an Array, or
192
192
  # a single string, comma separated.
193
193
  #
data/lib/jamf/client.rb CHANGED
@@ -179,7 +179,7 @@ module Jamf
179
179
  #
180
180
  def self.jamf_plist
181
181
  return {} unless JAMF_PLIST.file?
182
- JSS.parse_plist JAMF_PLIST
182
+ Jamf.parse_plist JAMF_PLIST
183
183
  end
184
184
 
185
185
  # All the JAMF receipts on this client
@@ -232,7 +232,7 @@ module Jamf
232
232
  #
233
233
  def self.hardware_data
234
234
  raw = `/usr/sbin/system_profiler SPHardwareDataType -xml 2>/dev/null`
235
- JSS.parse_plist(raw)[0]['_items'][0]
235
+ Jamf.parse_plist(raw)[0]['_items'][0]
236
236
  end
237
237
 
238
238
  # Who's currently got an active GUI session? - might be
@@ -284,7 +284,7 @@ module Jamf
284
284
  myudid = udid
285
285
  nc_prefs_file = Pathname.new "#{home}/#{USER_PREFS_BYHOST_FOLDER}/com.apple.notificationcenterui.#{myudid}.plist"
286
286
  return nil unless nc_prefs_file.readable?
287
- JSS.parse_plist(nc_prefs_file)['doNotDisturb']
287
+ Jamf.parse_plist(nc_prefs_file)['doNotDisturb']
288
288
  end
289
289
 
290
290
  # The home dir of the specified user, nil if
@@ -94,7 +94,7 @@ module JSS
94
94
  #
95
95
  def self.set_mgmt_action_ncprefs_flags(user, flags, hup: true)
96
96
  plist = Pathname.new "/Users/#{user}/Library/Preferences/#{NCPREFS_DOMAIN}.plist"
97
- prefs = JSS.parse_plist plist
97
+ prefs = Jamf.parse_plist plist
98
98
  mgmt_action_setting = prefs['apps'].select { |a| a['bundle-id'] == MGMT_ACTION_BUNDLE_ID }.first
99
99
  if mgmt_action_setting
100
100
  orig_flags = mgmt_action_setting['flags']
@@ -103,8 +103,7 @@ module JSS
103
103
  orig_flags = flags
104
104
  prefs['apps'] << { 'bundle-id' => MGMT_ACTION_BUNDLE_ID, 'flags' => flags }
105
105
  end
106
- # system "/usr/bin/defaults write #{NCPREFS_DOMAIN} '#{prefs.to_plist}'"
107
- plist.open('w') { |f| f.write prefs.to_plist }
106
+ plist.open('w') { |f| f.write Jamf.xml_plist_from(prefs) }
108
107
  system HUP_NOTIF_CTR_CMD if hup
109
108
  orig_flags
110
109
  end
data/lib/jamf/composer.rb CHANGED
@@ -126,7 +126,7 @@ module Jamf
126
126
  ###
127
127
  comp_plist_out = Pathname.new "/tmp/#{PKG_BUNDLE_ID_PFX}-#{pkg_filename}.plist"
128
128
  system "#{PKGBUILD} --analyze --root '#{root}' '#{comp_plist_out}'"
129
- comp_plist = Plist.parse_xml comp_plist_out.read
129
+ comp_plist = Jamf.parse_plist comp_plist_out
130
130
 
131
131
  ### if the plist is empty, there are no bundles in the pkg
132
132
  if comp_plist[0].nil?
@@ -141,7 +141,7 @@ module Jamf
141
141
  bndl['BundleHasStrictIdentifier'] = false
142
142
  end
143
143
  ### write out the edits
144
- comp_plist_out.open('w') { |f| f.write comp_plist.to_plist }
144
+ comp_plist_out.open('w') { |f| f.write Jamf.xml_plist_from(comp_plist) }
145
145
  comp_plist_arg = "--component-plist '#{comp_plist_out}'"
146
146
  end
147
147
 
data/lib/jamf/utility.rb CHANGED
@@ -187,19 +187,26 @@ module Jamf
187
187
  { stringform: valstr, arrayform: valarr }
188
188
  end # to_s_and_a
189
189
 
190
- # Parse a plist into a Ruby data structure.
191
- # This enhances Plist::parse_xml taking file paths, as well as XML Strings
192
- # and reading the files regardless of binary/XML format.
190
+ # Parse a plist into a Ruby data structure. The plist parameter may be
191
+ # a String containing an XML plist, or a path to a plist file, or it may be
192
+ # a Pathname object pointing to a plist file. The plist files may be XML or
193
+ # binary.
193
194
  #
194
195
  # @param plist[Pathname, String] the plist XML, or the path to a plist file
195
196
  #
197
+ # @param symbol_keys[Boolean] should any Hash keys in the result be converted
198
+ # into Symbols rather than remain as Strings?
199
+ #
196
200
  # @return [Object] the parsed plist as a ruby hash,array, etc.
197
201
  #
198
- def self.parse_plist(plist)
202
+ def self.parse_plist(plist, symbol_keys: false)
203
+ require 'cfpropertylist'
204
+
199
205
  # did we get a string of xml, or a string pathname?
200
206
  case plist
201
207
  when String
202
- return Plist.parse_xml plist if plist.include? '</plist>'
208
+ return CFPropertyList.native_types(CFPropertyList::List.new(data: plist).value, symbol_keys) if plist.include? '</plist>'
209
+
203
210
  plist = Pathname.new plist
204
211
  when Pathname
205
212
  true
@@ -208,11 +215,32 @@ module Jamf
208
215
  end # case plist
209
216
 
210
217
  # if we're here, its a Pathname
211
- raise Jamf::MissingDataError, "No such file: #{plist}" unless plist.file?
218
+ raise JSS::MissingDataError, "No such file: #{plist}" unless plist.file?
212
219
 
213
- Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape(plist.to_s)}`.force_encoding('UTF-8')
220
+ CFPropertyList.native_types(CFPropertyList::List.new(file: plist).value, symbol_keys)
214
221
  end # parse_plist
215
222
 
223
+ # Convert any ruby data to an XML plist.
224
+ #
225
+ # NOTE: Binary data is tricky. Easiest way is to pass in a
226
+ # Pathname or IO object (anything that responds to `read` and
227
+ # returns a bytestring)
228
+ # and then the CFPropertyList.guess method will read it and
229
+ # convert it to a Plist <data> element with base64 encoded
230
+ # data.
231
+ # For more info, see CFPropertyList.guess
232
+ #
233
+ # @param data [Object] the data to be converted, usually a Hash
234
+ #
235
+ # @return [String] the object converted into an XML plist
236
+ #
237
+ def self.xml_plist_from(data)
238
+ require 'cfpropertylist'
239
+ plist = CFPropertyList::List.new
240
+ plist.value = CFPropertyList.guess(data, convert_unknown_to_string: true)
241
+ plist.to_str(CFPropertyList::List::FORMAT_XML)
242
+ end
243
+
216
244
  # TODO: Sill needed in Jamf API?
217
245
  #
218
246
  # Converts anything that responds to #to_s to a Time, or nil
data/lib/jss.rb CHANGED
@@ -60,7 +60,6 @@ module JSS
60
60
  ### Gems
61
61
  require 'faraday'
62
62
  require 'faraday_middleware'
63
- require 'plist'
64
63
  require 'immutable-struct'
65
64
  require 'recursive-open-struct'
66
65
 
@@ -922,6 +922,7 @@ module JSS
922
922
  when 409
923
923
  err = JSS::ConflictError
924
924
  @last_http_response.body =~ /<p>(The server has not .*?)(<|$)/m
925
+ Regexp.last_match(1) || @last_http_response.body =~ %r{<p>Error: (.*?)</p>}
925
926
  msg = Regexp.last_match(1)
926
927
  when 400
927
928
  err = JSS::BadRequestError
@@ -123,7 +123,7 @@ module JSS
123
123
  #
124
124
  # @return [Hash] the parsed payloads plist.
125
125
  def parsed_payloads
126
- Plist.parse_xml @payloads
126
+ JSS.parse_plist @payloads
127
127
  end
128
128
 
129
129
  # @return [Array<Hash>] the individual payloads from the payload Plist
@@ -151,7 +151,7 @@ module JSS
151
151
  def payload_content=(new_content)
152
152
  payload_plist_data = parsed_payloads
153
153
  payload_plist_data['PayloadContent'] = new_content
154
- @payloads = payload_plist_data.to_plist
154
+ @payloads = JSS.xml_plist_from new_content
155
155
  @need_to_update = true
156
156
  @update_payloads = true
157
157
  end
@@ -435,9 +435,10 @@ module JSS
435
435
  @starting_address <= thing.range.begin && @ending_address >= thing.range.end
436
436
  else
437
437
  thing = IPAddr.new thing.to_s
438
- range.include? thing
438
+ range.cover? thing
439
439
  end
440
440
  end
441
+ alias cover? include?
441
442
 
442
443
  ### Does this network segment equal another?
443
444
  ### equality means the ranges are equal
@@ -459,7 +460,7 @@ module JSS
459
460
  ### @return [void]
460
461
  ###
461
462
  def building=(newval)
462
- new =
463
+ new =
463
464
  if newval.to_s.empty?
464
465
  JSS::BLANK
465
466
  else
@@ -546,7 +547,7 @@ module JSS
546
547
  ### @return [void]
547
548
  ###
548
549
  def netboot_server=(newval)
549
- new =
550
+ new =
550
551
  if newval.to_s.empty?
551
552
  JSS::BLANK
552
553
  else
@@ -555,7 +556,7 @@ module JSS
555
556
 
556
557
  JSS::NetbootServer.map_all_ids_to(:name)[id]
557
558
  end
558
-
559
+
559
560
  @netboot_server = new
560
561
  @need_to_update = true
561
562
  end
@@ -567,7 +568,7 @@ module JSS
567
568
  ### @return [void]
568
569
  ###
569
570
  def swu_server=(newval)
570
- new =
571
+ new =
571
572
  if newval.to_s.empty?
572
573
  JSS::BLANK
573
574
  else
@@ -287,6 +287,9 @@ module JSS
287
287
  # How is the category stored in the API data?
288
288
  CATEGORY_DATA_TYPE = Hash
289
289
 
290
+ # All valid script parameters
291
+ SCRIPT_PARAMETERS_AVAILABLE = %i[parameter4 parameter5 parameter6 parameter7 parameter8 parameter9 parameter10 parameter11].freeze
292
+
290
293
  # Class Methods
291
294
  ######################
292
295
 
@@ -1485,6 +1488,53 @@ module JSS
1485
1488
  removed
1486
1489
  end
1487
1490
 
1491
+ # Set a script parameter
1492
+ #
1493
+ # @param identifier [Integer,String] identifier the id or name of a script in this policy
1494
+ #
1495
+ # @param opts [Hash] opts the options to alter for this script
1496
+ #
1497
+ # @option [String] parameter4: the value of the 4th parameter passed to the script. this
1498
+ # overrides the same parameter in the script object itself.
1499
+ #
1500
+ # @option [String] parameter5: the value of the 5th parameter passed to the script. this
1501
+ # overrides the same parameter in the script object itself.
1502
+ #
1503
+ # @option [String] parameter6: the value of the 6th parameter passed to the script. this
1504
+ # overrides the same parameter in the script object itself.
1505
+ #
1506
+ # @option [String] parameter7: the value of the 7th parameter passed to the script. this
1507
+ # overrides the same parameter in the script object itself.
1508
+ #
1509
+ # @option [String] parameter8: the value of the 8th parameter passed to the script. this
1510
+ # overrides the same parameter in the script object itself.
1511
+ #
1512
+ # @option [String] parameter9: the value of the 9th parameter passed to the script. this
1513
+ # overrides the same parameter in the script object itself.
1514
+ #
1515
+ # @option [String] parameter10: the value of the 10th parameter passed to the script. this
1516
+ # overrides the same parameter in the script object itself.
1517
+ #
1518
+ # @option [String] parameter11: the value of the 11th parameter passed to the script. this
1519
+ # overrides the same parameter in the script object itself.
1520
+ #
1521
+ # @return [Array] the scripts array
1522
+ #
1523
+ def set_script_parameters(identifier, **opts)
1524
+ id = JSS::Script.valid_id identifier, api: @api
1525
+ raise JSS::NoSuchItemError, "No script matches '#{identifier}'" unless id
1526
+
1527
+ script_data = @scripts.select { |s| s[:id] == id }[0]
1528
+ raise JSS::InvalidDataError, "Script #{id} is not configured. Use add_script method." unless script_data
1529
+
1530
+ opts.each do |parameter, value|
1531
+ script_data[parameter] = value if SCRIPT_PARAMETERS_AVAILABLE.include? parameter
1532
+ end
1533
+
1534
+ @need_to_update = true
1535
+ @scripts
1536
+ end # end set_script_parameter
1537
+
1488
1538
  ###### Directory Bindings
1489
1539
 
1490
1540
  # @return [Array] the id's of the directory_bindings handled by the policy
@@ -1,94 +1,91 @@
1
- ### Copyright 2020 Pixar
2
-
3
- ###
4
- ### Licensed under the Apache License, Version 2.0 (the "Apache License")
5
- ### with the following modification; you may not use this file except in
6
- ### compliance with the Apache License and the following modification to it:
7
- ### Section 6. Trademarks. is deleted and replaced with:
8
- ###
9
- ### 6. Trademarks. This License does not grant permission to use the trade
10
- ### names, trademarks, service marks, or product names of the Licensor
11
- ### and its affiliates, except as required to comply with Section 4(c) of
12
- ### the License and to reproduce the content of the NOTICE file.
13
- ###
14
- ### You may obtain a copy of the Apache License at
15
- ###
16
- ### http://www.apache.org/licenses/LICENSE-2.0
17
- ###
18
- ### Unless required by applicable law or agreed to in writing, software
19
- ### distributed under the Apache License with the above modification is
20
- ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
- ### KIND, either express or implied. See the Apache License for the specific
22
- ### language governing permissions and limitations under the Apache License.
23
- ###
24
- ###
25
-
26
- ###
1
+ # Copyright 2020 Pixar
2
+
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "Apache License")
5
+ # with the following modification; you may not use this file except in
6
+ # compliance with the Apache License and the following modification to it:
7
+ # Section 6. Trademarks. is deleted and replaced with:
8
+ #
9
+ # 6. Trademarks. This License does not grant permission to use the trade
10
+ # names, trademarks, service marks, or product names of the Licensor
11
+ # and its affiliates, except as required to comply with Section 4(c) of
12
+ # the License and to reproduce the content of the NOTICE file.
13
+ #
14
+ # You may obtain a copy of the Apache License at
15
+ #
16
+ # http://www.apache.org/licenses/LICENSE-2.0
17
+ #
18
+ # Unless required by applicable law or agreed to in writing, software
19
+ # distributed under the Apache License with the above modification is
20
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21
+ # KIND, either express or implied. See the Apache License for the specific
22
+ # language governing permissions and limitations under the Apache License.
23
+ #
24
+ #
25
+
26
+ #
27
27
  module JSS
28
28
 
29
- ### Module Constants
29
+ # Module Constants
30
30
  #####################################
31
31
 
32
- ### Module Variables
32
+ # Module Variables
33
33
  #####################################
34
34
 
35
- ### Module Methods
35
+ # Module Methods
36
36
  #####################################
37
37
 
38
- ### Classes
38
+ # Classes
39
39
  #####################################
40
40
 
41
- ### A Script in the JSS.
42
- ###
43
- ### As of Casper 9.4, the script contents as stored in the database are
44
- ### accessible via the API
45
- ###
46
- ### This class will save the script contents back to the database with
47
- ### the {#create} or {#update} methods
48
- ###
49
- ### If your scripts are stored on the master distribution point instead of
50
- ### the database, you can use {#upload_master_file} to save it to the server,
51
- ### and {#delete_master_file} to delete it from the server.
52
- ###
53
- ### Use the {#run} method to run the script on the local machine via the 'jamf runScript' command
54
- ###
55
- ### @see JSS::APIObject
56
- ###
41
+ # A Script in the JSS.
42
+ #
43
+ # As of Casper 9.4, the script contents as stored in the database are
44
+ # accessible via the API
45
+ #
46
+ # According to Jamf as of early 2021, it has been some years now since
47
+ # its been possible to store script contents on a dist. point - they
48
+ # are all always in the database.
49
+ #
50
+ # Use the {#run} method to run the script on the local machine.
51
+ #
52
+ # @see JSS::APIObject
53
+ #
57
54
  class Script < JSS::APIObject
58
55
 
59
- ### Mix-Ins
56
+ # Mix-Ins
60
57
  #####################################
61
58
 
62
59
  include JSS::Creatable
63
60
  include JSS::Updatable
64
61
  include JSS::Categorizable
65
62
 
66
- ### Class Methods
63
+ # Class Methods
67
64
  #####################################
68
65
 
69
- ### Class Constants
66
+ # Class Constants
70
67
  #####################################
71
68
 
72
- ### The base for REST resources of this class
69
+ # The base for REST resources of this class
73
70
  RSRC_BASE = 'scripts'.freeze
74
71
 
75
- ### the hash key used for the JSON list output of all objects in the JSS
72
+ # the hash key used for the JSON list output of all objects in the JSS
76
73
  RSRC_LIST_KEY = :scripts
77
74
 
78
- ### The hash key used for the JSON object output.
79
- ### It's also used in various error messages
75
+ # The hash key used for the JSON object output.
76
+ # It's also used in various error messages
80
77
  RSRC_OBJECT_KEY = :script
81
78
 
82
- ### The script storage folder on the distribution point, if used
79
+ # The script storage folder on the distribution point, if used
83
80
  DIST_POINT_SCRIPTS_FOLDER = 'Scripts'.freeze
84
81
 
85
- ### Priority to use for running the script in relation to other actions during imaging
82
+ # Priority to use for running the script in relation to other actions during imaging
86
83
  PRIORITIES = ['Before', 'After', 'At Reboot'].freeze
87
84
 
88
- ### which is default?
85
+ # which is default?
89
86
  DEFAULT_PRIORITY = 'After'.freeze
90
87
 
91
- ### The keys used in the @parameters Hash
88
+ # The keys used in the @parameters Hash
92
89
  PARAMETER_KEYS = [:parameter4, :parameter5, :parameter6, :parameter7, :parameter8, :parameter9, :parameter10, :parameter11].freeze
93
90
 
94
91
  # the object type for this object in
@@ -103,34 +100,39 @@ module JSS
103
100
  CATEGORY_DATA_TYPE = String
104
101
 
105
102
 
106
- ### Attributes
103
+ # Attributes
107
104
  #####################################
108
105
 
109
- ### @return [String] the file name of the script, if stored in a dist. point
106
+ # @return [String] the file name of the script, if stored in a dist. point
110
107
  attr_reader :filename
111
108
 
112
- ### @return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
109
+ # @return [Array<String>] the OS versions this can be installed onto. For all minor versions, the format is 10.5.x
113
110
  attr_reader :os_requirements
114
111
 
115
- ### @return [String] either 'Before' or 'After' or "At Reboot".
112
+ # @return [String] either 'Before' or 'After' or "At Reboot".
116
113
  attr_reader :priority
117
114
 
118
- ### @return [String] the info field for this script
115
+ # @return [String] the info field for this script
119
116
  attr_reader :info
120
117
 
121
- ### @return [String] the notes field for this script
118
+ # @return [String] the notes field for this script
122
119
  attr_reader :notes
123
120
 
124
- ### @return [Hash] script parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
121
+ # @return [Hash] descriptions of parameters 4-11. Parameters 1-3 are predefined as target drive, computer name, and username
125
122
  attr_reader :parameters
123
+ alias parameter_labels parameters
124
+ alias parameter_descriptions parameters
126
125
 
127
- ### @return {String] the actual code for this script, if it's stored in the database.
126
+ # @return {String] the actual code for this script, if it's stored in the database.
128
127
  attr_reader :script_contents
129
128
 
130
- ### Constructor
129
+ # @return [String] the code for this script, Base64-encoded
130
+ attr_reader :script_contents_encoded
131
+
132
+ # Constructor
131
133
  #####################################
132
134
 
133
- ###
135
+ #
134
136
  def initialize(args = {})
135
137
  super
136
138
 
@@ -141,19 +143,23 @@ module JSS
141
143
  @parameters = @init_data[:parameters] ? @init_data[:parameters] : {}
142
144
  @priority = @init_data[:priority] || DEFAULT_PRIORITY
143
145
  @script_contents = @init_data[:script_contents]
146
+ @script_contents_encoded = @init_data[:script_contents_encoded]
147
+ if @script_contents && @script_contents_encoded.to_s.empty?
148
+ @script_contents_encoded = Base64.encode64 @script_contents
149
+ end
144
150
  end # initialize
145
151
 
146
- ### Change the script filename
147
- ###
148
- ### Setting it to nil will make it match the script name
149
- ###
150
- ### @param new_val[String,Nil] the new filename
151
- ###
152
- ### @return [void]
153
- ###
154
- ### @note This method does NOT change the filename on the distribution point
155
- ### if that's where you store your scripts.
156
- ###
152
+ # Change the script filename
153
+ #
154
+ # Setting it to nil will make it match the script name
155
+ #
156
+ # @param new_val[String,Nil] the new filename
157
+ #
158
+ # @return [void]
159
+ #
160
+ # @note This method does NOT change the filename on the distribution point
161
+ # if that's where you store your scripts.
162
+ #
157
163
  def filename=(new_val)
158
164
  new_val = nil if new_val == ''
159
165
  new_val = @name unless new_val
@@ -164,14 +170,14 @@ module JSS
164
170
  @need_to_update = true
165
171
  end # filename=
166
172
 
167
- ### Change the script's display name
168
- ###
169
- ### If the filename is the same as the name, the filename will be changed also
170
- ###
171
- ### @param new_val[String] the new display name
172
- ###
173
- ### @return [void]
174
- ###
173
+ # Change the script's display name
174
+ #
175
+ # If the filename is the same as the name, the filename will be changed also
176
+ #
177
+ # @param new_val[String] the new display name
178
+ #
179
+ # @return [void]
180
+ #
175
181
  def name=(new_val)
176
182
  new_val = new_val.to_s
177
183
  return if new_val == @name
@@ -179,39 +185,39 @@ module JSS
179
185
  raise JSS::MissingDataError, "Name can't be empty" if new_val.empty?
180
186
  raise JSS::AlreadyExistsError, "A script already exists with the name '#{new_val}'" if JSS::Script.all_names.include? new_val
181
187
 
182
- ### if the filename matches the name, change that too.
188
+ # if the filename matches the name, change that too.
183
189
  @filename = new_val if @filename == @name
184
190
  @name = new_val
185
191
 
186
- ### if our REST resource is based on the name, update that too
192
+ # if our REST resource is based on the name, update that too
187
193
  @rest_rsrc = "#{RSRC_BASE}/name/#{CGI.escape @name.to_s}" if @rest_rsrc.include? '/name/'
188
194
  @need_to_update = true
189
195
  end # name=
190
196
 
191
- ### Change the os_requirements
192
- ###
193
- ### Minumum OS's can be specified as a string using the notation ">=10.6.7"
194
- ### See the {JSS.expand_min_os} method for details.
195
- ###
196
- ### @param new_val[String, Array<String>] the new os requirements as a comma-separted String or an Array of Strings
197
- ###
198
- ### @return [void]
199
- ###
200
- ### @example String value
201
- ### myscript.os_requirements "10.5, 10.5.3, 10.6.x"
202
- ###
203
- ### @example Array value
204
- ### ok_oses = ['10.5', '10.5.3', '10.6.x']
205
- ### myscript.os_requirements ok_oses
206
- ###
207
- ### @example Minimum OS
208
- ### myscript.os_requirements ">=10.7.5"
209
- ###
197
+ # Change the os_requirements
198
+ #
199
+ # Minumum OS's can be specified as a string using the notation ">=10.6.7"
200
+ # See the {JSS.expand_min_os} method for details.
201
+ #
202
+ # @param new_val[String, Array<String>] the new os requirements as a comma-separted String or an Array of Strings
203
+ #
204
+ # @return [void]
205
+ #
206
+ # @example String value
207
+ # myscript.os_requirements "10.5, 10.5.3, 10.6.x"
208
+ #
209
+ # @example Array value
210
+ # ok_oses = ['10.5', '10.5.3', '10.6.x']
211
+ # myscript.os_requirements ok_oses
212
+ #
213
+ # @example Minimum OS
214
+ # myscript.os_requirements ">=10.7.5"
215
+ #
210
216
  def os_requirements=(new_val)
211
- ### nil should be an empty array
217
+ # nil should be an empty array
212
218
  new_val = [] if new_val.to_s.empty?
213
219
 
214
- ### if any value starts with >=, expand it
220
+ # if any value starts with >=, expand it
215
221
  case new_val
216
222
  when String
217
223
  new_val = JSS.expand_min_os(new_val) if new_val =~ /^>=/
@@ -223,17 +229,17 @@ module JSS
223
229
  raise JSS::InvalidDataError, 'os_requirements must be a String or an Array of strings'
224
230
  end # case
225
231
 
226
- ### get the array version
232
+ # get the array version
227
233
  @os_requirements = JSS.to_s_and_a(new_val)[:arrayform]
228
234
  @need_to_update = true
229
235
  end # os_requirements=
230
236
 
231
- ### Change the priority of this script
232
- ###
233
- ### @param new_val[Integer] the new priority, which must be one of {PRIORITIES}
234
- ###
235
- ### @return [void]
236
- ###
237
+ # Change the priority of this script
238
+ #
239
+ # @param new_val[Integer] the new priority, which must be one of {PRIORITIES}
240
+ #
241
+ # @return [void]
242
+ #
237
243
  def priority=(new_val)
238
244
  return nil if new_val == @priority
239
245
  new_val = DEFAULT_PRIORITY if new_val.nil? || (new_val == '')
@@ -242,47 +248,47 @@ module JSS
242
248
  @need_to_update = true
243
249
  end # priority=
244
250
 
245
- ### Change the info field
246
- ###
247
- ### @param new_val[String] the new info
248
- ###
249
- ### @return [void]
250
- ###
251
+ # Change the info field
252
+ #
253
+ # @param new_val[String] the new info
254
+ #
255
+ # @return [void]
256
+ #
251
257
  def info=(new_val)
252
258
  return nil if new_val == @info
253
- ### line breaks should be \r
259
+ # line breaks should be \r
254
260
  new_val = new_val.to_s.tr("\n", "\r")
255
261
  @info = new_val
256
262
  @need_to_update = true
257
263
  end # info=
258
264
 
259
- ### Change the notes field
260
- ###
261
- ### @param new_val[String] the new notes
262
- ###
263
- ### @return [void]
264
- ###
265
+ # Change the notes field
266
+ #
267
+ # @param new_val[String] the new notes
268
+ #
269
+ # @return [void]
270
+ #
265
271
  def notes=(new_val)
266
272
  return nil if new_val == @notes
267
- ### line breaks should be \r
273
+ # line breaks should be \r
268
274
  new_val = new_val.to_s.tr("\n", "\r")
269
275
  @notes = new_val
270
276
  @need_to_update = true
271
277
  end # notes=
272
278
 
273
- ### Replace all the script parameters at once.
274
- ###
275
- ### This will replace the entire set with the hash provided.
276
- ###
277
- ### @param new_val[Hash] the Hash keys must exist in {PARAMETER_KEYS}
278
- ###
279
- ### @return [void]
280
- ###
279
+ # Replace all the script parameters at once.
280
+ #
281
+ # This will replace the entire set with the hash provided.
282
+ #
283
+ # @param new_val[Hash] the Hash keys must exist in {PARAMETER_KEYS}
284
+ #
285
+ # @return [void]
286
+ #
281
287
  def parameters=(new_val)
282
288
  return nil if new_val == @parameters
283
289
  new_val = {} if new_val.nil? || (new_val == '')
284
290
 
285
- ### check the values
291
+ # check the values
286
292
  raise JSS::InvalidDataError, ':parameters must be a Hash with keys :parameter4 thru :parameter11' unless \
287
293
  new_val.is_a?(Hash) && ((new_val.keys & PARAMETER_KEYS) == new_val.keys)
288
294
  new_val.each do |_k, v|
@@ -293,14 +299,14 @@ module JSS
293
299
  @need_to_update = true
294
300
  end # parameters=
295
301
 
296
- ### Change one of the stored parameters
297
- ###
298
- ### @param param_num[Integer] which param are we setting? must be 4..11
299
- ###
300
- ### @param new_val[String] the new value for the parameter
301
- ###
302
- ### @return [void]
303
- ###
302
+ # Change one of the stored parameters
303
+ #
304
+ # @param param_num[Integer] which param are we setting? must be 4..11
305
+ #
306
+ # @param new_val[String] the new value for the parameter
307
+ #
308
+ # @return [void]
309
+ #
304
310
  def set_parameter(param_num, new_val)
305
311
  raise JSS::NoSuchItemError, 'Parameter numbers must be from 4-11' unless (4..11).cover? param_num
306
312
  pkey = "parameter#{param_num}".to_sym
@@ -309,21 +315,23 @@ module JSS
309
315
  @parameters[pkey] = new_val
310
316
  @need_to_update = true
311
317
  end
312
-
313
- ### Change the executable code of this script.
314
- ###
315
- ### If the arg is a Pathname instance, or a String starting with "/"
316
- ### Then the arg is assumed to be a file from which to read the code.
317
- ###
318
- ### Otherwise it should be a String with the code itself, and it must start with '#!"
319
- ###
320
- ### After doing this, use {#create} or {#update} to write it to the database or
321
- ### use {#upload_master_file} to save it to the master dist. point.
322
- ###
323
- ### @param new_val[String,Pathname] the new script contents or a path to a file containing it.
324
- ###
325
- ### @return [void]
326
- ###
318
+ alias set_parameter_label set_parameter
319
+ alias set_parameter_description set_parameter
320
+
321
+ # Change the executable code of this script.
322
+ #
323
+ # If the arg is a Pathname instance, or a String starting with "/"
324
+ # Then the arg is assumed to be a file from which to read the code.
325
+ #
326
+ # Otherwise it should be a String with the code itself, and it must start with '#!"
327
+ #
328
+ # After doing this, use {#create} or {#update} to write it to the database or
329
+ # use {#upload_master_file} to save it to the master dist. point.
330
+ #
331
+ # @param new_val[String,Pathname] the new script contents or a path to a file containing it.
332
+ #
333
+ # @return [void]
334
+ #
327
335
  def script_contents=(new_val)
328
336
  new_code = case new_val
329
337
  when String
@@ -341,207 +349,89 @@ module JSS
341
349
  raise JSS::InvalidDataError, "Script contents must start with '#!'" unless new_code.start_with? '#!'
342
350
 
343
351
  @script_contents = new_code
352
+ @script_contents_encoded = Base64.encode64 @script_contents
344
353
  @need_to_update = true
345
354
  end
346
355
 
347
- ### Save the @script_contents for this script to a file on the Master Distribution point.
348
- ###
349
- ### If you'll be uploading several files you can specify unmount as false, and do it manually when all
350
- ### are finished.
351
- ###
352
- ### use {#script_contents=} to set the script_contents from a String or Pathname
353
- ###
354
- ### @param rw_pw[String] the password for the read/write account on the master Distribution Point
355
- ###
356
- ### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
357
- ###
358
- ### @return [void]
359
- ###
360
- def upload_master_file(rw_pw, unmount = true)
361
- raise JSS::MissingDataError, 'No code specified. Use #code= first.' if @script_contents.nil? || @script_contents.empty?
362
-
363
- mdp = JSS::DistributionPoint.master_distribution_point
364
- raise JSS::InvaldDatatError, 'Incorrect password for read-write access to master distribution point.' unless mdp.check_pw :rw, rw_pw
365
-
366
- destination = mdp.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
367
- destination.save @script_contents
368
- mdp.unmount if unmount
369
- end # upload
370
-
371
- ### Delete the filename from the master distribution point, if it exists.
372
- ###
373
- ### If you'll be uploading several files you can specify unmount as false, and do it manually when all
374
- ### are finished.
375
- ###
376
- ### @param rw_pw[String] the password for the read/write account on the master Distribution Point
377
- ###
378
- ### @param unmount[Boolean] whether or not ot unount the distribution point when finished.
379
- ###
380
- ### @return [Boolean] was the file deleted?
381
- ###
382
- def delete_master_file(rw_pw, unmount = true)
383
- file = JSS::DistributionPoint.master_distribution_point.mount(rw_pw, :rw) + "#{DIST_POINT_SCRIPTS_FOLDER}/#{@filename}"
384
- if file.exist?
385
- file.delete
386
- did_it = true
387
- else
388
- did_it = false
389
- end # if exists
390
- JSS::DistributionPoint.master_distribution_point.unmount if unmount
391
- did_it
392
- end
393
-
394
- ### Run this script on the current machine using the "jamf runScript" command.
395
- ###
396
- ### If the script code is available in the {#script_contents} attribute, then that
397
- ### code is saved to a tmp file, and executed. Otherwise, the script is assumed
398
- ### to be stored on the distribution point.
399
- ###
400
- ### If the dist. point has http downloads enabled, then the URL is used as the path with the
401
- ### 'jamf runScript' command.
402
- ###
403
- ### If http is not an option, the dist.point is mounted, and the script copied locally before running.
404
- ### In this case the options must include :ro_pw => 'somepass'
405
- ### to provide the read-only password for mounting the distribution point. If :unmount => true
406
- ### is provided, the dist. point will be unmounted immediately after copying
407
- ### the script locally. Otherwise it will remain mounted, in case there's further need of it.
408
- ###
409
- ### Any local on-disk copies of the script are removed after running.
410
- ###
411
- ### After the script runs, this method returns a two-item Array.
412
- ### - the first item is an Integer, the exit status of the script itself (0 means success)
413
- ### - the second item is a String, the output (stdout + stderr) of the jamf binary, which will include
414
- ### the script output.
415
- ### The exit status of the jamf binary process will be available as a Process::Status object
416
- ### in $? immediately after running.
417
- ###
418
- ### @param opts[Hash] the options for running the script
419
- ###
420
- ### @option opts :target[String,Pathname] the 'target drive', passed to the script as the first commandline option.
421
- ### Defaults to '/'
422
- ###
423
- ### @option opts :computer_name[String] the name of the computer, passed to the script as the second commandline
424
- ### option. Defaults to the name of the current machine
425
- ###
426
- ### @option opts :username[String] the username to be passed to the script as the third commandline option.
427
- ###
428
- ### @option opts :p1..:p8[String] the values to be passed as the 4th - 11th commandline options, overriding
429
- ### those defined with the script in the JSS
430
- ###
431
- ### @option opts :ro_pw[String] the read-only password for mounting the distribution point, if needed
432
- ###
433
- ### @option opts :unmount[Boolean} should the dist. point be unmounted when finished, if we mounted it?
434
- ###
435
- ### @option opts :verbose[Boolean] should the 'jamf runScript' command be verbose?
436
- ###
437
- ### @option opts :show_output[Boolean] should the output (stdout + stderr) of 'jamf runScript' be copied to
438
- ### stdout in realtime, as well as returned?
439
- ###
440
- ### @return [Array<(Integer,String)>] the exit status of the *script* and stdout+stderr of 'jamf runScript'.
441
- ### The exit status of the jamf binary will be available in $? immediately after running.
442
- ###
443
- ### *NOTE* In the WEB UI and API, the definable parameters are numbered 4-11, since 1, 2, & 3 are the
444
- ### target drive, computer name, and user name respectively. However, the jamf binary refers to them as
445
- ### p1-p8, and that's how they are expected as options to #run. So if :p1=> "new param" is given as an
446
- ### aption to #run, it will override any value that the API provided in @parameters[:parameter4]
447
- ###
356
+ # Run this script on the current machine.
357
+ #
358
+ # If the script code is available in the {#script_contents} attribute, then that
359
+ # code is saved to a tmp file, and executed. The tmp file is deleted immediately
360
+ # after running
361
+ #
362
+ # After the script runs, this method returns a two-item Array.
363
+ # - the first item is an Integer, the exit status of the script itself (0 means success)
364
+ # - the second item is a String, the output (stdout + stderr) of the script.
365
+ #
366
+ # The exit status of the jamf binary process will be available as a Process::Status object
367
+ # in $? immediately after running.
368
+ #
369
+ # @param opts[Hash] the options for running the script
370
+ #
371
+ # @option opts :target[String,Pathname] the 'target drive', passed to the script as the first commandline option.
372
+ # Defaults to '/'
373
+ #
374
+ # @option opts :computer_name[String] the name of the computer, passed to the script as the second commandline
375
+ # option. Defaults to the name of the current machine
376
+ #
377
+ # @option opts :username[String] the username to be passed to the script as the third commandline option.
378
+ # Defaults to the current console user.
379
+ #
380
+ # @option opts :p4..:p11[String] the values to be passed as the 4th - 11th commandline params
381
+ # Script params 1, 2, & 3 are the target:, computer_name: and username: params
382
+ #
383
+ # @option opts :show_output[Boolean] should the output (stdout + stderr) be copied to
384
+ # stdout in realtime, as well as returned?
385
+ #
386
+ # @return [Array<(Integer,String)>] the exit status and stdout+stderr of the script
387
+ #
448
388
  def run(opts = {})
449
- opts[:target] ||= '/'
450
- opts[:p1] ||= @parameters[:parameter4]
451
- opts[:p2] ||= @parameters[:parameter5]
452
- opts[:p3] ||= @parameters[:parameter6]
453
- opts[:p4] ||= @parameters[:parameter7]
454
- opts[:p5] ||= @parameters[:parameter8]
455
- opts[:p6] ||= @parameters[:parameter9]
456
- opts[:p7] ||= @parameters[:parameter10]
457
- opts[:p8] ||= @parameters[:parameter11]
458
-
459
- dp_mount_pt = nil
460
- delete_exec = false
461
-
462
- begin
463
-
464
- # do we have the code already? if so, save it out and make it executable
465
- if @script_contents && !@script_contents.empty?
466
-
467
- script_path = JSS::Client::DOWNLOADS_FOLDER
468
-
469
- executable = script_path + @filename
470
-
471
- executable.jss_touch
472
- executable.chmod 0o700
473
- executable.jss_save @script_contents
474
- delete_exec = true
475
-
476
- # otherwise, get it from the dist. point
477
- else
478
- dist_point = JSS::DistributionPoint.my_distribution_point api: @api
389
+ raise JSS::MissingDataError, 'script_contents does not start with #!' unless @script_contents.to_s.start_with? '#!'
479
390
 
480
- ### how do we access our dist. point?
481
- if dist_point.http_downloads_enabled
482
- script_path = dist_point.http_url + "/#{DIST_POINT_SCRIPTS_FOLDER}/"
483
-
484
- else
485
- dp_mount_pt = dist_point.mount opts[:ro_pw]
486
-
487
- script_path = (dp_mount_pt + DIST_POINT_SCRIPTS_FOLDER)
488
-
489
- end # if http enabled
490
-
491
- end # if @script_contents and (not @script_contents.empty?)
492
-
493
- # build the command as an array.
494
- command_arry = ['-script', @filename, '-path', script_path.to_s]
495
-
496
- command_arry << '-target'
497
- command_arry << opts[:target].to_s
498
-
499
- command_arry << '-computerName' if opts[:computer_name]
500
- command_arry << opts[:computer_name] if opts[:computer_name]
501
-
502
- command_arry << '-username' if opts[:username]
503
- command_arry << opts[:username] if opts[:username]
504
-
505
- command_arry << '-p1' if opts[:p1]
506
- command_arry << opts[:p1] if opts[:p1]
507
-
508
- command_arry << '-p2' if opts[:p2]
509
- command_arry << opts[:p2] if opts[:p2]
510
-
511
- command_arry << '-p3' if opts[:p3]
512
- command_arry << opts[:p3] if opts[:p3]
513
-
514
- command_arry << '-p4' if opts[:p4]
515
- command_arry << opts[:p4] if opts[:p4]
516
-
517
- command_arry << '-p5' if opts[:p5]
518
- command_arry << opts[:p5] if opts[:p5]
391
+ opts[:target] ||= '/'
392
+ opts[:computer_name] ||= JSS::Client.run_jamf('getComputerName')[/>(.)</, 1]
393
+ opts[:username] ||= JSS::Client.console_user
519
394
 
520
- command_arry << '-p6' if opts[:p6]
521
- command_arry << opts[:p6] if opts[:p6]
395
+ params = [opts[:target], opts[:computer_name], opts[:username]]
396
+ params << opts[:p4]
397
+ params << opts[:p5]
398
+ params << opts[:p6]
399
+ params << opts[:p7]
400
+ params << opts[:p8]
401
+ params << opts[:p9]
402
+ params << opts[:p10]
403
+ params << opts[:p11]
522
404
 
523
- command_arry << '-p7' if opts[:p7]
524
- command_arry << opts[:p7] if opts[:p7]
405
+ # everything must be a string
406
+ params.map! &:to_s
525
407
 
526
- command_arry << '-p8' if opts[:p8]
527
- command_arry << opts[:p8] if opts[:p8]
408
+ # remove nils
409
+ params.compact!
528
410
 
529
- command_arry << '-verbose' if opts[:verbose]
411
+ # remove empty strings
412
+ params.delete_if &:empty?
530
413
 
531
- command = command_arry.shelljoin
414
+ return_value = []
532
415
 
533
- jamf_output = JSS::Client.run_jamf 'runScript', command, opts[:show_output]
416
+ # Save and run the script from a private temp dir
417
+ # which will be deleted when finished
418
+ require 'tmpdir'
419
+ Dir.mktmpdir do |dir|
420
+ executable = Pathname.new "#{dir}/#{@name}"
421
+ executable.jss_touch
422
+ executable.chmod 0o700
423
+ executable.jss_save @script_contents
534
424
 
535
- jamf_output =~ /^.*Script exit code: (\d+)(\D|$)/
425
+ cmd = [executable.to_s]
426
+ cmd += params
536
427
 
537
- script_exitstatus = Regexp.last_match(1).to_i
428
+ stdout_and_stderr_str, status = Open3.capture2e(*cmd)
538
429
 
539
- ensure
540
- executable.delete if delete_exec && executable.exist?
541
- dist_point.unmount if dp_mount_pt && dp_mount_pt.mountpoint? && opts[:unmount]
542
- end # begin/ensure
430
+ return_value << status.exitstatus
431
+ return_value << stdout_and_stderr_str
432
+ end # Dir.mktmpdirs
543
433
 
544
- [script_exitstatus, jamf_output]
434
+ return_value
545
435
  end # def run
546
436
 
547
437
  # aliases under their methods seem to confuse the YARD documenter, so I'm putting them all here.
@@ -552,13 +442,13 @@ module JSS
552
442
  alias contents script_contents
553
443
  alias contents= script_contents=
554
444
 
555
- ### Private Instance Methods
445
+ # Private Instance Methods
556
446
  #####################################
557
447
 
558
448
  private
559
449
 
560
- ### Return the xml for creating or updating this script in the JSS
561
- ###
450
+ # Return the xml for creating or updating this script in the JSS
451
+ #
562
452
  def rest_xml
563
453
  doc = REXML::Document.new
564
454
  scpt = doc.add_element 'script'
@@ -579,7 +469,7 @@ module JSS
579
469
  PARAMETER_KEYS.each { |p| pars.add_element(p.to_s).text = @parameters[p] }
580
470
  end
581
471
 
582
- scpt.add_element('script_contents_encoded').text = Base64.encode64(@script_contents)
472
+ scpt.add_element('script_contents_encoded').text = script_contents_encoded
583
473
 
584
474
  doc.to_s
585
475
  end # rest xml