ruby-jss 0.8.1 → 0.8.2
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.
- 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
|