ruby-jss 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ruby-jss might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +6 -0
- data/lib/jss/api_object.rb +2 -1
- data/lib/jss/utility.rb +334 -344
- data/lib/jss/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ba0289a3600d2d044211e9b13ee210fb6c10b7a2
|
4
|
+
data.tar.gz: 647f502e548e3f86345ce9992e20ec26fbe1a273
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfebb20f13d49cd7677bc9e5fbb47ab668d5a98640cfdd3a4e97852c2cd22189c563cad9759972c5932e047ab31fdeb3cad9dce84c80c67f7e9c7e33f1f8d0f8
|
7
|
+
data.tar.gz: fb8761e60f48733df1f6047a4b1b324a1ece25d6c7dad6693b2a162cdd08d981921df20efb475716996f887b43f10b4c17c5029146387ca528199da9c3583950
|
data/CHANGES.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# Change History
|
2
2
|
|
3
|
+
## v0.8.1 2017-06-07
|
4
|
+
|
5
|
+
- Fix: Some objects failed to locate their 'main subset' (the chunk of API data that contains the object name and id) correctly.
|
6
|
+
- Fix: Some versions of Gem::Version don't like dashes (which are part of SemVers).
|
7
|
+
|
8
|
+
|
3
9
|
## v0.8.1 2017-06-05
|
4
10
|
|
5
11
|
- Improvement: Support for the new semantic versioning of Jamf products starting with Jamf Pro 9.99
|
data/lib/jss/api_object.rb
CHANGED
@@ -721,11 +721,12 @@ module JSS
|
|
721
721
|
# @return [Hash] The part of the @init_data containg the :id and :name
|
722
722
|
#
|
723
723
|
def find_main_subset
|
724
|
+
return @init_data if @init_data[:id] && @init_data[:name]
|
725
|
+
return @init_data[:general] if @init_data[:general] && @init_data[:general][:id] && @init_data[:general][:name]
|
724
726
|
@init_data.each do |key, value|
|
725
727
|
next unless value.is_a? Hash
|
726
728
|
return value if value.keys.include?(:id) && value.keys.include?(:name)
|
727
729
|
end
|
728
|
-
@init_data
|
729
730
|
end
|
730
731
|
|
731
732
|
# parse category data during initialization
|
data/lib/jss/utility.rb
CHANGED
@@ -1,134 +1,132 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
1
|
+
# Copyright 2017 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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
def self.expand_min_os
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
### (and hope apple doesn't do more than 15 maint releases for an OS)
|
29
|
+
# A collection of useful utility methods. Mostly for
|
30
|
+
# converting values between formats, parsing data, and
|
31
|
+
# user interaction.
|
32
|
+
|
33
|
+
# Converts an OS Version into an Array of higher OS versions.
|
34
|
+
#
|
35
|
+
# It's unlikely that this library will still be in use as-is by the release of OS X 10.19.15.
|
36
|
+
# Hopefully well before then JAMF will implement a "minimum OS" in the JSS itself.
|
37
|
+
#
|
38
|
+
# @param min_os [String] the mimimum OS version to expand, e.g. ">=10.6.7" or "10.6.7"
|
39
|
+
#
|
40
|
+
# @return [Array] Nearly all potential OS versions from the minimum to 10.19.x.
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# JSS.expand_min_os ">=10.6.7" # => returns this array
|
44
|
+
# # ["10.6.7",
|
45
|
+
# # "10.6.8",
|
46
|
+
# # "10.6.9",
|
47
|
+
# # "10.6.10",
|
48
|
+
# # "10.6.11",
|
49
|
+
# # "10.6.12",
|
50
|
+
# # "10.6.13",
|
51
|
+
# # "10.6.14",
|
52
|
+
# # "10.6.15",
|
53
|
+
# # "10.7.x",
|
54
|
+
# # "10.8.x",
|
55
|
+
# # "10.9.x",
|
56
|
+
# # "10.10.x",
|
57
|
+
# # "10.11.x",
|
58
|
+
# # "10.12.x",
|
59
|
+
# # "10.13.x",
|
60
|
+
# # "10.14.x",
|
61
|
+
# # "10.15.x",
|
62
|
+
# # "10.16.x",
|
63
|
+
# # "10.17.x",
|
64
|
+
# # "10.18.x",
|
65
|
+
# # "10.19.x"]
|
66
|
+
#
|
67
|
+
#
|
68
|
+
def self.expand_min_os(min_os)
|
69
|
+
min_os = min_os.delete '>='
|
70
|
+
|
71
|
+
# split the version into major, minor and maintenance release numbers
|
72
|
+
(maj, min, maint) = min_os.split('.')
|
73
|
+
maint = 'x' if maint.nil? || maint == '0'
|
74
|
+
|
75
|
+
# if the maint release number is an "x" just start the list of OK OS's with it
|
76
|
+
if maint == 'x'
|
77
|
+
ok_oses = [maj + '.' + min.to_s + '.x']
|
78
|
+
|
79
|
+
# otherwise, start with it and explicitly add all maint releases up to 15
|
80
|
+
# (and hope apple doesn't do more than 15 maint releases for an OS)
|
82
81
|
else
|
83
82
|
ok_oses = []
|
84
83
|
(maint.to_i..15).each do |m|
|
85
|
-
ok_oses <<
|
84
|
+
ok_oses << maj + '.' + min + '.' + m.to_s
|
86
85
|
end # each m
|
87
86
|
end
|
88
87
|
|
89
|
-
|
90
|
-
|
88
|
+
# now account for all OS X versions starting with 10.
|
89
|
+
# up to at least 10.19.x
|
91
90
|
((min.to_i + 1)..19).each do |v|
|
92
|
-
ok_oses <<
|
91
|
+
ok_oses << maj + '.' + v.to_s + '.x'
|
93
92
|
end # each v
|
94
|
-
|
93
|
+
ok_oses
|
95
94
|
end
|
96
95
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
96
|
+
# Scripts and packages can have processor limitations.
|
97
|
+
# This method tests a given processor, against a requirement
|
98
|
+
# to see if the requirement is met.
|
99
|
+
#
|
100
|
+
# @param requirement[String] The processor requirement.
|
101
|
+
# either 'ppc', 'x86', or some variation on "none", nil, or empty
|
102
|
+
#
|
103
|
+
# @param processor[String] the processor to check, defaults to
|
104
|
+
# the processor of the current machine. Any flavor of intel
|
106
105
|
## is (i486, i386, x86-64, etc) is treated as "x86"
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
def self.processor_ok?
|
112
|
-
|
113
|
-
return true if requirement.to_s.empty? or requirement =~ /none/i
|
106
|
+
#
|
107
|
+
# @return [Boolean] can this pkg be installed with the processor
|
108
|
+
# given?
|
109
|
+
#
|
110
|
+
def self.processor_ok?(requirement, processor = nil)
|
111
|
+
return true if requirement.to_s.empty? || requirement =~ /none/i
|
114
112
|
processor ||= `/usr/bin/uname -p`
|
115
|
-
|
113
|
+
requirement == (processor.to_s.include?('86') ? 'x86' : 'ppc')
|
116
114
|
end
|
117
115
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
def self.os_ok?
|
116
|
+
# Scripts and packages can have OS limitations.
|
117
|
+
# This method tests a given OS, against a requirement list
|
118
|
+
# to see if the requirement is met.
|
119
|
+
#
|
120
|
+
# @param requirement[String,Array] The os requirement list, a comma-seprated string
|
121
|
+
# or array of strings of allows OSes. e.g. 10.7, 10.8.5 or 10.9.x
|
122
|
+
#
|
123
|
+
# @param processor[String] the os to check, defaults to
|
124
|
+
# the os of the current machine.
|
125
|
+
#
|
126
|
+
# @return [Boolean] can this pkg be installed with the processor
|
127
|
+
# given?
|
128
|
+
#
|
129
|
+
def self.os_ok?(requirement, os_to_check = nil)
|
132
130
|
return true if requirement.to_s =~ /none/i
|
133
131
|
return true if requirement.to_s == 'n'
|
134
132
|
requirement = JSS.to_s_and_a(requirement)[:arrayform]
|
@@ -143,164 +141,158 @@ module JSS
|
|
143
141
|
# "10.8.x" /^10\.8\.?\d*$/
|
144
142
|
req_regexps = requirement.map do |r|
|
145
143
|
if r.end_with?('.x')
|
146
|
-
/^#{r.chomp('.x').gsub('.','\.')}\.?\d*$/
|
144
|
+
/^#{r.chomp('.x').gsub('.', '\.')}\.?\d*$/
|
147
145
|
|
148
146
|
elsif r =~ /^\d+\.\d+$/
|
149
|
-
/^#{r.gsub('.','\.')}(.0)?$/
|
147
|
+
/^#{r.gsub('.', '\.')}(.0)?$/
|
150
148
|
|
151
149
|
else
|
152
|
-
/^#{r.gsub('.','\.')}$/
|
150
|
+
/^#{r.gsub('.', '\.')}$/
|
153
151
|
end
|
154
152
|
end
|
155
153
|
|
156
|
-
req_regexps.each{|re| return true if os_to_check =~ re
|
157
|
-
|
154
|
+
req_regexps.each { |re| return true if os_to_check =~ re }
|
155
|
+
false
|
158
156
|
end
|
159
157
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
def self.to_s_and_a (somedata)
|
158
|
+
# Given a list of data as a comma-separated string, or an Array of strings,
|
159
|
+
# return a Hash with both versions.
|
160
|
+
#
|
161
|
+
# Some parts of the JSS require lists as comma-separated strings, while
|
162
|
+
# often those data are easier work with as arrays. This method is a handy way
|
163
|
+
# to get either form when given either form.
|
164
|
+
#
|
165
|
+
# @param somedata [String, Array] the data to parse, of either class,
|
166
|
+
#
|
167
|
+
# @return [Hash{:stringform => String, :arrayform => Array}] the data as both comma-separated String and Array
|
168
|
+
#
|
169
|
+
# @example
|
170
|
+
# JSS.to_s_and_a "foo, bar, baz" # Hash => {:stringform => "foo, bar, baz", :arrayform => ["foo", "bar", "baz"]}
|
171
|
+
#
|
172
|
+
# JSS.to_s_and_a ["foo", "bar", "baz"] # Hash => {:stringform => "foo, bar, baz", :arrayform => ["foo", "bar", "baz"]}
|
173
|
+
#
|
174
|
+
def self.to_s_and_a(somedata)
|
178
175
|
case somedata
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
176
|
+
when nil
|
177
|
+
valstr = ''
|
178
|
+
valarr = []
|
179
|
+
when String
|
180
|
+
valstr = somedata
|
181
|
+
valarr = somedata.split(/,\s*/)
|
182
|
+
when Array
|
183
|
+
valstr = somedata.join ', '
|
184
|
+
valarr = somedata
|
185
|
+
else
|
186
|
+
raise JSS::InvalidDataError, 'Input must be a comma-separated String or an Array of Strings'
|
190
187
|
end # case
|
191
|
-
|
188
|
+
{ stringform: valstr, arrayform: valarr }
|
192
189
|
end # to_s_and_a
|
193
190
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
def self.parse_plist
|
203
|
-
|
191
|
+
# Parse a plist into a Ruby data structure.
|
192
|
+
# This enhances Plist::parse_xml taking file paths, as well as XML Strings
|
193
|
+
# and reading the files regardless of binary/XML format.
|
194
|
+
#
|
195
|
+
# @param plist[Pathname, String] the plist XML, or the path to a plist file
|
196
|
+
#
|
197
|
+
# @return [Object] the parsed plist as a ruby hash,array, etc.
|
198
|
+
#
|
199
|
+
def self.parse_plist(plist)
|
204
200
|
# did we get a string of xml, or a string pathname?
|
205
201
|
case plist
|
206
202
|
when String
|
207
|
-
if plist.include?
|
208
|
-
|
209
|
-
else
|
210
|
-
plist = Pathname.new plist
|
211
|
-
end
|
203
|
+
return Plist.parse_xml plist if plist.include? '</plist>'
|
204
|
+
plist = Pathname.new plist
|
212
205
|
when Pathname
|
213
206
|
true
|
214
207
|
else
|
215
|
-
raise ArgumentError,
|
208
|
+
raise ArgumentError, 'Argument must be a path (as a Pathname or String) or a String of XML'
|
216
209
|
end # case plist
|
217
210
|
|
218
211
|
# if we're here, its a Pathname
|
219
212
|
raise JSS::MissingDataError, "No such file: #{plist}" unless plist.file?
|
220
213
|
|
221
|
-
|
222
|
-
|
214
|
+
Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape(plist.to_s)}`
|
223
215
|
end # parse_plist
|
224
216
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
###
|
217
|
+
# Converts anything that responds to #to_s to a Time, or nil
|
218
|
+
#
|
219
|
+
# Return nil if the item is nil, 0 or an empty String.
|
220
|
+
#
|
221
|
+
# Otherwise the item converted to a string, and parsed with DateTime.parse.
|
222
|
+
# It is then examined to see if it has a UTC offset. If not, the local offset
|
223
|
+
# is applied, then the DateTime is converted to a Time.
|
224
|
+
#
|
225
|
+
# @param a_datetime [#to_s] The thing to convert to a time.
|
226
|
+
#
|
227
|
+
# @return [Time, nil] nil is returned if a_datetime is nil, 0 or an empty String.
|
228
|
+
#
|
238
229
|
def self.parse_time(a_datetime)
|
239
230
|
return nil if NIL_DATES.include? a_datetime
|
240
231
|
|
241
232
|
the_dt = DateTime.parse(a_datetime.to_s)
|
242
233
|
|
243
|
-
|
244
|
-
|
234
|
+
# The microseconds in DateTimes are stored as a fraction of a day.
|
235
|
+
# Convert them to an integer of microseconds
|
245
236
|
usec = (the_dt.sec_fraction * 60 * 60 * 24 * (10**6)).to_i
|
246
237
|
|
247
|
-
|
248
|
-
|
249
|
-
if the_dt.offset
|
250
|
-
the_dt =
|
238
|
+
# if the UTC offset of the datetime is zero, make a new one with the correct local offset
|
239
|
+
# (which might also be zero if we happen to be in GMT)
|
240
|
+
if the_dt.offset.zero?
|
241
|
+
the_dt = DateTime.new(the_dt.year, the_dt.month, the_dt.day, the_dt.hour, the_dt.min, the_dt.sec, JSS::TIME_ZONE_OFFSET)
|
251
242
|
end
|
252
243
|
# now convert it to a Time and return it
|
253
244
|
Time.at the_dt.strftime('%s').to_i, usec
|
245
|
+
end # parse_time
|
246
|
+
|
247
|
+
# Deprecated - to be eventually removed in favor of
|
248
|
+
# the more-appropriately named JSS::parse_time
|
249
|
+
#
|
250
|
+
# @see JSS::parse_time
|
251
|
+
#
|
252
|
+
def self.parse_datetime(a_datetime)
|
253
|
+
parse_time(a_datetime)
|
254
|
+
end
|
254
255
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
###
|
262
|
-
def self.parse_datetime(a_datetime) ; self.parse_time(a_datetime) ; end
|
263
|
-
|
264
|
-
### Converts JSS epoch (unix epoch + milliseconds) to a Ruby Time object
|
265
|
-
###
|
266
|
-
### @param epoch[String, Integer, nil]
|
267
|
-
###
|
268
|
-
### @return [Time, nil] nil is returned if epoch is nil, 0 or an empty String.
|
269
|
-
###
|
256
|
+
# Converts JSS epoch (unix epoch + milliseconds) to a Ruby Time object
|
257
|
+
#
|
258
|
+
# @param epoch[String, Integer, nil]
|
259
|
+
#
|
260
|
+
# @return [Time, nil] nil is returned if epoch is nil, 0 or an empty String.
|
261
|
+
#
|
270
262
|
def self.epoch_to_time(epoch)
|
271
263
|
return nil if NIL_DATES.include? epoch
|
272
264
|
Time.at(epoch.to_i / 1000.0)
|
273
|
-
end #parse_date
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
265
|
+
end # parse_date
|
266
|
+
|
267
|
+
# Given a string of xml element text, escape any characters that would make XML unhappy.
|
268
|
+
# * & => &
|
269
|
+
# * " => "
|
270
|
+
# * < => <
|
271
|
+
# * > => >
|
272
|
+
# * ' => '
|
273
|
+
#
|
274
|
+
# @param string [String] the string to make xml-compliant.
|
275
|
+
#
|
276
|
+
# @return [String] the xml-compliant string
|
277
|
+
#
|
286
278
|
def self.escape_xml(string)
|
287
279
|
string.gsub(/&/, '&').gsub(/\"/, '"').gsub(/>/, '>').gsub(/</, '<').gsub(/'/, ''')
|
288
280
|
end
|
289
281
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
def self.array_to_rexml_array(element,list)
|
303
|
-
raise JSS::InvalidDataError,
|
282
|
+
# Given an element name and an array of content, generate an Array of
|
283
|
+
# REXML::Element objects with that name, and matching content.
|
284
|
+
# The array of REXML elements would render thus:
|
285
|
+
# <foo>bar</foo>
|
286
|
+
# <foo>morefoo</foo>
|
287
|
+
#
|
288
|
+
# @param element [#to_s] an element_name like :foo
|
289
|
+
#
|
290
|
+
# @param list [Array<#to_s>] an Array of element content such as ["bar", :morefoo]
|
291
|
+
#
|
292
|
+
# @return [Array<REXML::Element>]
|
293
|
+
#
|
294
|
+
def self.array_to_rexml_array(element, list)
|
295
|
+
raise JSS::InvalidDataError, 'Arg. must be an Array.' unless list.is_a? Array
|
304
296
|
element = element.to_s
|
305
297
|
list.map do |v|
|
306
298
|
e = REXML::Element.new(element)
|
@@ -309,26 +301,26 @@ module JSS
|
|
309
301
|
end
|
310
302
|
end
|
311
303
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
304
|
+
# Given a simple Hash, convert it to an array of REXML Elements such that each
|
305
|
+
# key becomes an element, and its value becomes the text content of
|
306
|
+
# that element
|
307
|
+
#
|
308
|
+
# @example
|
309
|
+
# my_hash = {:foo => "bar", :baz => :morefoo}
|
310
|
+
# xml = JSS.hash_to_rexml_array(my_hash)
|
311
|
+
# xml.each{|x| puts x }
|
312
|
+
#
|
313
|
+
# <foo>bar</foo>
|
314
|
+
# <baz>morefoo</baz>
|
315
|
+
#
|
316
|
+
# @param hash [Hash{#to_s => #to_s}] the Hash to convert
|
317
|
+
#
|
318
|
+
# @return [Array<REXML::Element>] the Array of REXML elements.
|
319
|
+
#
|
328
320
|
def self.hash_to_rexml_array(hash)
|
329
|
-
raise InvalidDataError,
|
321
|
+
raise InvalidDataError, 'Arg. must be a Hash.' unless hash.is_a? Hash
|
330
322
|
ary = []
|
331
|
-
hash.each_pair do |k,v|
|
323
|
+
hash.each_pair do |k, v|
|
332
324
|
el = REXML::Element.new k.to_s
|
333
325
|
el.text = v
|
334
326
|
ary << el
|
@@ -336,39 +328,39 @@ module JSS
|
|
336
328
|
ary
|
337
329
|
end
|
338
330
|
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
def self.item_list_to_rexml_list(list_element, item_element
|
331
|
+
# Given an Array of Hashes with :id and/or :name keys, return
|
332
|
+
# a single REXML element with a sub-element for each item,
|
333
|
+
# each of which contains a :name or :id element.
|
334
|
+
#
|
335
|
+
# @param list_element [#to_s] the name of the XML element that contains the list.
|
336
|
+
# e.g. :computers
|
337
|
+
#
|
338
|
+
# @param item_element [#to_s] the name of each XML element in the list,
|
339
|
+
# e.g. :computer
|
340
|
+
#
|
341
|
+
# @param item_list [Array<Hash>] an Array of Hashes each with a :name or :id key.
|
342
|
+
#
|
343
|
+
# @param content [Symbol] which hash key should be used as the content of if list item? Defaults to :name
|
344
|
+
#
|
345
|
+
# @return [REXML::Element] the item list as REXML
|
346
|
+
#
|
347
|
+
# @example
|
348
|
+
# comps = [{:id=>2,:name=>'kimchi'},{:id=>5,:name=>'mantis'}]
|
349
|
+
# xml = JSS.item_list_to_rexml_list(:computers, :computer , comps, :name)
|
350
|
+
# puts xml
|
351
|
+
# # output manually formatted for clarity. No newlines in the real xml string
|
352
|
+
# <computers>
|
353
|
+
# <computer>
|
354
|
+
# <name>kimchi</name>
|
355
|
+
# </computer>
|
356
|
+
# <computer>
|
357
|
+
# <name>mantis</name>
|
358
|
+
# </computer>
|
359
|
+
# </computers>
|
360
|
+
#
|
361
|
+
# # if content is :id, then, eg. <name>kimchi</name> would be <id>2</id>
|
362
|
+
#
|
363
|
+
def self.item_list_to_rexml_list(list_element, item_element, item_list, content = :name)
|
372
364
|
xml_list = REXML::Element.new list_element.to_s
|
373
365
|
item_list.each do |i|
|
374
366
|
xml_list.add_element(item_element.to_s).add_element(content.to_s).text = i[content]
|
@@ -376,41 +368,41 @@ module JSS
|
|
376
368
|
xml_list
|
377
369
|
end
|
378
370
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
371
|
+
# Parse a JSS Version number into something comparable.
|
372
|
+
#
|
373
|
+
# With Jamf Pro 9.99, "Semantic Versioning" is used, see http://semver.org/
|
374
|
+
#
|
375
|
+
# For versions less than 9.99 parsing is like this:
|
376
|
+
# - Digits before the first dot are the Major Version
|
377
|
+
# - The first digit after the first dot is the Minor Version
|
378
|
+
# - Any other digits after the first dot but before a non-digit
|
379
|
+
# are the Revision
|
380
|
+
# - Anything after a second dot is the build identifier
|
381
|
+
# - Any non-digit anywhere means that it and everything after it
|
382
|
+
# are the build identifier
|
383
|
+
#
|
384
|
+
# So 9.32 becomes major-9, minor-3, rev-2, build-''
|
385
|
+
# and 9.32.3764 becomes major-9, minor-3, rev-2, build-3764
|
386
|
+
# and 9.32a3764 becomes major-9, minor-3, rev-2, build-a3764
|
387
|
+
# and 9.32a1234.t234 becomes major-9, minor-3, rev-2, build-a1234.t234
|
388
|
+
#
|
389
|
+
# This old style method of parsing will break if digits between the first
|
390
|
+
# dot and the second (or the end) ever gets above 99, since '100' will
|
391
|
+
# become minor-1, rev-0
|
392
|
+
#
|
393
|
+
# This method returns a Hash with these keys:
|
394
|
+
# * :major => the major version, Integer
|
395
|
+
# * :minor => the minor version, Integor
|
396
|
+
# * :maint => the revision, Integer
|
397
|
+
# (this is also available with the keys :patch and :revision)
|
398
|
+
# * :build => the revision, String
|
399
|
+
# * :version => a Gem::Version object built from :major, :minor, :revision
|
400
|
+
# which can be easily compared with other Gem::Version objects.
|
401
|
+
#
|
402
|
+
# @param version[String] a JSS version number from the API
|
403
|
+
#
|
404
|
+
# @return [Hash{Symbol => String, Gem::Version}] the parsed version data.
|
405
|
+
#
|
414
406
|
def self.parse_jss_version(version)
|
415
407
|
major, second_part, *_rest = version.split('.')
|
416
408
|
raise JSS::InvalidDataError, 'JSS Versions must start with "x.x" where x is one or more digits' unless major =~ /\d$/ && second_part =~ /^\d/
|
@@ -434,11 +426,11 @@ module JSS
|
|
434
426
|
maint: revision.to_i,
|
435
427
|
patch: revision.to_i,
|
436
428
|
build: build,
|
437
|
-
version: Gem::Version.new(version)
|
429
|
+
# version: Gem::Version.new(version)
|
430
|
+
version: Gem::Version.new("#{major}.#{minor}.#{revision}#{build}")
|
438
431
|
}
|
439
432
|
end # parse_jss_version_oldstyle
|
440
433
|
|
441
|
-
|
442
434
|
# (see parse_jss_version)
|
443
435
|
def self.parse_jss_version_oldstyle(version)
|
444
436
|
version =~ /^(\d+?)\.(.*)$/
|
@@ -475,49 +467,47 @@ module JSS
|
|
475
467
|
}
|
476
468
|
end # parse_jss_version_oldstyle
|
477
469
|
|
478
|
-
|
479
|
-
|
470
|
+
# @return [Boolean] is this code running as root?
|
471
|
+
#
|
480
472
|
def self.superuser?
|
481
|
-
Process.euid
|
473
|
+
Process.euid.zero?
|
482
474
|
end
|
483
475
|
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
476
|
+
# Retrive one or all lines from whatever was piped to standard input.
|
477
|
+
#
|
478
|
+
# Standard input is read completely the first time this method is called
|
479
|
+
# and the lines are stored as an Array in the module var @stdin_lines
|
480
|
+
#
|
481
|
+
# @param line[Integer] which line of stdin is being retrieved.
|
482
|
+
# The default is zero (0) which returns all of stdin as a single string.
|
483
|
+
#
|
484
|
+
# @return [String, nil] the requested ling of stdin, or nil if it doesn't exist.
|
485
|
+
#
|
494
486
|
def self.stdin(line = 0)
|
495
|
-
|
487
|
+
@stdin_lines ||= ($stdin.tty? ? [] : $stdin.read.lines.map { |l| l.chomp("\n") })
|
496
488
|
|
497
|
-
return
|
489
|
+
return @stdin_lines.join("\n") if line <= 0
|
498
490
|
idx = line - 1
|
499
|
-
|
491
|
+
@stdin_lines[idx]
|
500
492
|
end
|
501
493
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
494
|
+
# Prompt for a password in a terminal.
|
495
|
+
#
|
496
|
+
# @param message [String] the prompt message to display
|
497
|
+
#
|
498
|
+
# @return [String] the text typed by the user
|
499
|
+
#
|
508
500
|
def self.prompt_for_password(message)
|
509
|
-
|
510
501
|
begin
|
511
502
|
$stdin.reopen '/dev/tty' unless $stdin.tty?
|
512
503
|
$stderr.print "#{message} "
|
513
|
-
system
|
504
|
+
system '/bin/stty -echo'
|
514
505
|
pw = $stdin.gets.chomp("\n")
|
515
506
|
puts
|
516
507
|
ensure
|
517
|
-
system
|
508
|
+
system '/bin/stty echo'
|
518
509
|
end # begin
|
519
|
-
|
510
|
+
pw
|
520
511
|
end
|
521
512
|
|
522
|
-
|
523
513
|
end # module
|
data/lib/jss/version.rb
CHANGED
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: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Lasell
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-06-
|
12
|
+
date: 2017-06-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: plist
|