ruby-jss 1.2.3 → 1.2.4a1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/jamf.rb +169 -0
- data/lib/jamf/api/abstract_classes/collection_resource.rb +422 -0
- data/lib/jamf/api/abstract_classes/generic_reference.rb +145 -0
- data/lib/jamf/api/abstract_classes/json_object.rb +1074 -0
- data/lib/jamf/api/abstract_classes/prestage.rb +219 -0
- data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +126 -0
- data/lib/jamf/api/abstract_classes/resource.rb +250 -0
- data/lib/jamf/api/abstract_classes/singleton_resource.rb +87 -0
- data/lib/jamf/api/attribute_classes/ip_address.rb +66 -0
- data/lib/jamf/api/attribute_classes/timestamp.rb +144 -0
- data/lib/jamf/api/connection.rb +734 -0
- data/lib/jamf/api/connection/api_error.rb +111 -0
- data/lib/jamf/api/connection/api_error_styleguide.rb +96 -0
- data/lib/jamf/api/connection/token.rb +220 -0
- data/lib/jamf/api/json_objects/account_prefs.rb +79 -0
- data/lib/jamf/api/json_objects/android_details.rb +139 -0
- data/lib/jamf/api/json_objects/appletv_details.rb +110 -0
- data/lib/jamf/api/json_objects/attachment.rb +68 -0
- data/lib/jamf/api/json_objects/cellular_network.rb +151 -0
- data/lib/jamf/api/json_objects/change_log_entry.rb +77 -0
- data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +67 -0
- data/lib/jamf/api/json_objects/country.rb +51 -0
- data/lib/jamf/api/json_objects/extension_attribute_value.rb +128 -0
- data/lib/jamf/api/json_objects/installed_application.rb +59 -0
- data/lib/jamf/api/json_objects/installed_certificate.rb +53 -0
- data/lib/jamf/api/json_objects/installed_configuration_profile.rb +67 -0
- data/lib/jamf/api/json_objects/installed_ebook.rb +58 -0
- data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +59 -0
- data/lib/jamf/api/json_objects/inventory_preload_extension_attribute.rb +52 -0
- data/lib/jamf/api/json_objects/ios_details.rb +244 -0
- data/lib/jamf/api/json_objects/location.rb +95 -0
- data/lib/jamf/api/json_objects/md_prestage_name.rb +57 -0
- data/lib/jamf/api/json_objects/md_prestage_names.rb +82 -0
- data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +165 -0
- data/lib/jamf/api/json_objects/mobile_device_details.rb +219 -0
- data/lib/jamf/api/json_objects/mobile_device_security.rb +101 -0
- data/lib/jamf/api/json_objects/prestage_assignment.rb +61 -0
- data/lib/jamf/api/json_objects/prestage_location.rb +104 -0
- data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +132 -0
- data/lib/jamf/api/json_objects/prestage_scope.rb +54 -0
- data/lib/jamf/api/json_objects/prestage_sync_status.rb +63 -0
- data/lib/jamf/api/json_objects/purchasing_data.rb +125 -0
- data/lib/jamf/api/mixins/abstract.rb +58 -0
- data/lib/jamf/api/mixins/bulk_deletable.rb +39 -0
- data/lib/jamf/api/mixins/change_log.rb +136 -0
- data/lib/jamf/api/mixins/extendable.rb +75 -0
- data/lib/jamf/api/mixins/immutable.rb +39 -0
- data/lib/jamf/api/mixins/locatable.rb +124 -0
- data/lib/jamf/api/mixins/lockable.rb +48 -0
- data/lib/jamf/api/mixins/referable.rb +92 -0
- data/lib/jamf/api/mixins/searchable.rb +202 -0
- data/lib/jamf/api/mixins/uncreatable.rb +40 -0
- data/lib/jamf/api/mixins/undeletable.rb +40 -0
- data/lib/jamf/api/resources/collection_resources/account.rb +163 -0
- data/lib/jamf/api/resources/collection_resources/building.rb +114 -0
- data/lib/jamf/api/resources/collection_resources/category.rb +82 -0
- data/lib/jamf/api/resources/collection_resources/computer.rb +49 -0
- data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +80 -0
- data/lib/jamf/api/resources/collection_resources/department.rb +79 -0
- data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +45 -0
- data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +274 -0
- data/lib/jamf/api/resources/collection_resources/md_prestage.rb +139 -0
- data/lib/jamf/api/resources/collection_resources/mobile_device.rb +315 -0
- data/lib/jamf/api/resources/collection_resources/script.rb +190 -0
- data/lib/jamf/api/resources/collection_resources/site.rb +77 -0
- data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +131 -0
- data/lib/jamf/api/resources/singleton_resources/authorization.rb +88 -0
- data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +139 -0
- data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +95 -0
- data/lib/jamf/client.rb +301 -0
- data/lib/jamf/client/jamf_binary.rb +132 -0
- data/lib/jamf/client/jamf_helper.rb +298 -0
- data/lib/jamf/client/management_action.rb +114 -0
- data/lib/jamf/compatibility.rb +88 -0
- data/lib/jamf/composer.rb +190 -0
- data/lib/jamf/configuration.rb +281 -0
- data/lib/jamf/exceptions.rb +107 -0
- data/lib/jamf/ruby_extensions.rb +36 -0
- data/lib/jamf/ruby_extensions/array.rb +35 -0
- data/lib/jamf/ruby_extensions/array/predicates.rb +46 -0
- data/lib/jamf/ruby_extensions/array/utils.rb +47 -0
- data/lib/jamf/ruby_extensions/filetest.rb +32 -0
- data/lib/jamf/ruby_extensions/filetest/predicates.rb +46 -0
- data/lib/jamf/ruby_extensions/hash.rb +33 -0
- data/lib/jamf/ruby_extensions/hash/backports.rb +92 -0
- data/lib/jamf/ruby_extensions/ipaddr.rb +37 -0
- data/lib/jamf/ruby_extensions/ipaddr/utils.rb +95 -0
- data/lib/jamf/ruby_extensions/object.rb +30 -0
- data/lib/jamf/ruby_extensions/object/predicates.rb +51 -0
- data/lib/jamf/ruby_extensions/pathname.rb +39 -0
- data/lib/jamf/ruby_extensions/pathname/predicates.rb +50 -0
- data/lib/jamf/ruby_extensions/pathname/utils.rb +75 -0
- data/lib/jamf/ruby_extensions/string.rb +35 -0
- data/lib/jamf/ruby_extensions/string/backports.rb +66 -0
- data/lib/jamf/ruby_extensions/string/conversions.rb +65 -0
- data/lib/jamf/ruby_extensions/string/predicates.rb +47 -0
- data/lib/jamf/utility.rb +423 -0
- data/lib/jamf/validate.rb +224 -0
- data/lib/jamf/version.rb +32 -0
- data/lib/jpapi.rb +26 -0
- data/lib/jss/version.rb +1 -1
- metadata +104 -4
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Copyright 2019 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
|
+
module JamfRubyExtensions
|
|
25
|
+
|
|
26
|
+
module String
|
|
27
|
+
|
|
28
|
+
module Conversions
|
|
29
|
+
|
|
30
|
+
# Convert the strings "true" and "false"
|
|
31
|
+
# (after stripping whitespace and downcasing)
|
|
32
|
+
# to TrueClass and FalseClass respectively
|
|
33
|
+
#
|
|
34
|
+
# Return nil if any other string.
|
|
35
|
+
#
|
|
36
|
+
# @return [Boolean,nil] the boolean value
|
|
37
|
+
#
|
|
38
|
+
def j_to_bool
|
|
39
|
+
case strip.downcase
|
|
40
|
+
when 'true' then true
|
|
41
|
+
when 'false' then false
|
|
42
|
+
end # case
|
|
43
|
+
end # to bool
|
|
44
|
+
|
|
45
|
+
# Convert a string to a Jamf::Timestamp object
|
|
46
|
+
#
|
|
47
|
+
# @return [Time] the time represented by the string.
|
|
48
|
+
#
|
|
49
|
+
def j_to_timestamp
|
|
50
|
+
Jamf::Timestamp.new self
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Convert a String to a Pathname object
|
|
54
|
+
#
|
|
55
|
+
# @return [Pathname]
|
|
56
|
+
#
|
|
57
|
+
def j_to_pathname
|
|
58
|
+
Pathname.new self
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end # module
|
|
62
|
+
|
|
63
|
+
end # module
|
|
64
|
+
|
|
65
|
+
end # module
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Copyright 2019 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
|
+
module JamfRubyExtensions
|
|
27
|
+
|
|
28
|
+
module String
|
|
29
|
+
|
|
30
|
+
module Predicates
|
|
31
|
+
|
|
32
|
+
INTEGER_RE = /\A[0-9]+\Z/.freeze
|
|
33
|
+
|
|
34
|
+
# Is this string also a positive integer?
|
|
35
|
+
# (i.e. it consists only of numberic digits)
|
|
36
|
+
#
|
|
37
|
+
# @return [Boolean]
|
|
38
|
+
#
|
|
39
|
+
def j_integer?
|
|
40
|
+
self =~ INTEGER_RE ? true : false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end # module
|
|
44
|
+
|
|
45
|
+
end # module
|
|
46
|
+
|
|
47
|
+
end # module
|
data/lib/jamf/utility.rb
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# Copyright 2019 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
|
+
# The Module
|
|
27
|
+
module Jamf
|
|
28
|
+
|
|
29
|
+
# Constants
|
|
30
|
+
###################################
|
|
31
|
+
|
|
32
|
+
# These Utility constants are useful all over the place.
|
|
33
|
+
# Many of them are commonly used Strings.
|
|
34
|
+
|
|
35
|
+
BLANK = ''.freeze
|
|
36
|
+
|
|
37
|
+
UNDERSCORE = '_'.freeze
|
|
38
|
+
|
|
39
|
+
# A collection of useful utility methods. Mostly for
|
|
40
|
+
# converting values between formats, parsing data, and
|
|
41
|
+
# user interaction.
|
|
42
|
+
|
|
43
|
+
# TODO: confirm need for each method in Jamf Pro API.
|
|
44
|
+
|
|
45
|
+
# Converts an OS Version into an Array of higher OS versions.
|
|
46
|
+
#
|
|
47
|
+
# It's unlikely that this library will still be in use as-is by the release of OS X 10.30.20.
|
|
48
|
+
# Hopefully well before then JAMF will implement a "minimum OS" in the JSS itself.
|
|
49
|
+
#
|
|
50
|
+
# @param min_os [String] the mimimum OS version to expand, e.g. ">=10.6.7" or "10.6.7"
|
|
51
|
+
#
|
|
52
|
+
# @return [Array] Nearly all potential OS versions from the minimum to 10.19.x.
|
|
53
|
+
#
|
|
54
|
+
# @example
|
|
55
|
+
# JSS.expand_min_os ">=10.6.7" # => returns this array
|
|
56
|
+
# # ["10.6.7",
|
|
57
|
+
# # "10.6.8",
|
|
58
|
+
# # "10.6.9",
|
|
59
|
+
# # ...
|
|
60
|
+
# # "10.6.20",
|
|
61
|
+
# # "10.7.x",
|
|
62
|
+
# # "10.8.x",
|
|
63
|
+
# # ...
|
|
64
|
+
# # "10.30.x"]
|
|
65
|
+
#
|
|
66
|
+
#
|
|
67
|
+
def self.expand_min_os(min_os)
|
|
68
|
+
min_os = min_os.delete '>='
|
|
69
|
+
|
|
70
|
+
# split the version into major, minor and maintenance release numbers
|
|
71
|
+
(maj, min, maint) = min_os.split('.')
|
|
72
|
+
maint = 'x' if maint.nil? || maint == '0'
|
|
73
|
+
|
|
74
|
+
# if the maint release number is an "x" just start the list of OK OS's with it
|
|
75
|
+
if maint == 'x'
|
|
76
|
+
ok_oses = [maj + '.' + min.to_s + '.x']
|
|
77
|
+
|
|
78
|
+
# otherwise, start with it and explicitly add all maint releases up to 20
|
|
79
|
+
# (and hope apple doesn't do more than 20 maint releases for an OS)
|
|
80
|
+
else
|
|
81
|
+
ok_oses = []
|
|
82
|
+
(maint.to_i..20).each do |m|
|
|
83
|
+
ok_oses << maj + '.' + min + '.' + m.to_s
|
|
84
|
+
end # each m
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# now account for all OS X versions starting with 10.
|
|
88
|
+
# up to at least 10.30.x
|
|
89
|
+
((min.to_i + 1)..30).each do |v|
|
|
90
|
+
ok_oses << maj + '.' + v.to_s + '.x'
|
|
91
|
+
end # each v
|
|
92
|
+
ok_oses
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Scripts and packages can have processor limitations.
|
|
96
|
+
# This method tests a given processor, against a requirement
|
|
97
|
+
# to see if the requirement is met.
|
|
98
|
+
#
|
|
99
|
+
# @param requirement[String] The processor requirement.
|
|
100
|
+
# either 'ppc', 'x86', or some variation on "none", nil, or empty
|
|
101
|
+
#
|
|
102
|
+
# @param processor[String] the processor to check, defaults to
|
|
103
|
+
# the processor of the current machine. Any flavor of intel
|
|
104
|
+
## is (i486, i386, x86-64, etc) is treated as "x86"
|
|
105
|
+
#
|
|
106
|
+
# @return [Boolean] can this pkg be installed with the processor
|
|
107
|
+
# given?
|
|
108
|
+
#
|
|
109
|
+
def self.processor_ok?(requirement, processor = nil)
|
|
110
|
+
return true if requirement.to_s.empty? || requirement =~ /none/i
|
|
111
|
+
processor ||= `/usr/bin/uname -p`
|
|
112
|
+
requirement == (processor.to_s.include?('86') ? 'x86' : 'ppc')
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Scripts and packages can have OS limitations.
|
|
116
|
+
# This method tests a given OS, against a requirement list
|
|
117
|
+
# to see if the requirement is met.
|
|
118
|
+
#
|
|
119
|
+
# @param requirement[String,Array] The os requirement list, a comma-seprated string
|
|
120
|
+
# or array of strings of allows OSes. e.g. 10.7, 10.8.5 or 10.9.x
|
|
121
|
+
#
|
|
122
|
+
# @param processor[String] the os to check, defaults to
|
|
123
|
+
# the os of the current machine.
|
|
124
|
+
#
|
|
125
|
+
# @return [Boolean] can this pkg be installed with the processor
|
|
126
|
+
# given?
|
|
127
|
+
#
|
|
128
|
+
def self.os_ok?(requirement, os_to_check = nil)
|
|
129
|
+
return true if requirement.to_s =~ /none/i
|
|
130
|
+
return true if requirement.to_s == 'n'
|
|
131
|
+
requirement = JSS.to_s_and_a(requirement)[:arrayform]
|
|
132
|
+
return true if requirement.empty?
|
|
133
|
+
|
|
134
|
+
os_to_check ||= `/usr/bin/sw_vers -productVersion`.chomp
|
|
135
|
+
|
|
136
|
+
# convert the requirement array into an array of regexps.
|
|
137
|
+
# examples:
|
|
138
|
+
# "10.8.5" becomes /^10\.8\.5$/
|
|
139
|
+
# "10.8" becomes /^10.8(.0)?$/
|
|
140
|
+
# "10.8.x" /^10\.8\.?\d*$/
|
|
141
|
+
req_regexps = requirement.map do |r|
|
|
142
|
+
if r.end_with?('.x')
|
|
143
|
+
/^#{r.chomp('.x').gsub('.', '\.')}\.?\d*$/
|
|
144
|
+
|
|
145
|
+
elsif r =~ /^\d+\.\d+$/
|
|
146
|
+
/^#{r.gsub('.', '\.')}(.0)?$/
|
|
147
|
+
|
|
148
|
+
else
|
|
149
|
+
/^#{r.gsub('.', '\.')}$/
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
req_regexps.each { |re| return true if os_to_check =~ re }
|
|
154
|
+
false
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Given a list of data as a comma-separated string, or an Array of strings,
|
|
158
|
+
# return a Hash with both versions.
|
|
159
|
+
#
|
|
160
|
+
# Some parts of the JSS require lists as comma-separated strings, while
|
|
161
|
+
# often those data are easier work with as arrays. This method is a handy way
|
|
162
|
+
# to get either form when given either form.
|
|
163
|
+
#
|
|
164
|
+
# @param somedata [String, Array] the data to parse, of either class,
|
|
165
|
+
#
|
|
166
|
+
# @return [Hash{:stringform => String, :arrayform => Array}] the data as both comma-separated String and Array
|
|
167
|
+
#
|
|
168
|
+
# @example
|
|
169
|
+
# JSS.to_s_and_a "foo, bar, baz" # Hash => {:stringform => "foo, bar, baz", :arrayform => ["foo", "bar", "baz"]}
|
|
170
|
+
#
|
|
171
|
+
# JSS.to_s_and_a ["foo", "bar", "baz"] # Hash => {:stringform => "foo, bar, baz", :arrayform => ["foo", "bar", "baz"]}
|
|
172
|
+
#
|
|
173
|
+
def self.to_s_and_a(somedata)
|
|
174
|
+
case somedata
|
|
175
|
+
when nil
|
|
176
|
+
valstr = ''
|
|
177
|
+
valarr = []
|
|
178
|
+
when String
|
|
179
|
+
valstr = somedata
|
|
180
|
+
valarr = somedata.split(/,\s*/)
|
|
181
|
+
when Array
|
|
182
|
+
valstr = somedata.join ', '
|
|
183
|
+
valarr = somedata
|
|
184
|
+
else
|
|
185
|
+
raise Jamf::InvalidDataError, 'Input must be a comma-separated String or an Array of Strings'
|
|
186
|
+
end # case
|
|
187
|
+
{ stringform: valstr, arrayform: valarr }
|
|
188
|
+
end # to_s_and_a
|
|
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.
|
|
193
|
+
#
|
|
194
|
+
# @param plist[Pathname, String] the plist XML, or the path to a plist file
|
|
195
|
+
#
|
|
196
|
+
# @return [Object] the parsed plist as a ruby hash,array, etc.
|
|
197
|
+
#
|
|
198
|
+
def self.parse_plist(plist)
|
|
199
|
+
# did we get a string of xml, or a string pathname?
|
|
200
|
+
case plist
|
|
201
|
+
when String
|
|
202
|
+
return Plist.parse_xml plist if plist.include? '</plist>'
|
|
203
|
+
plist = Pathname.new plist
|
|
204
|
+
when Pathname
|
|
205
|
+
true
|
|
206
|
+
else
|
|
207
|
+
raise ArgumentError, 'Argument must be a path (as a Pathname or String) or a String of XML'
|
|
208
|
+
end # case plist
|
|
209
|
+
|
|
210
|
+
# if we're here, its a Pathname
|
|
211
|
+
raise Jamf::MissingDataError, "No such file: #{plist}" unless plist.file?
|
|
212
|
+
|
|
213
|
+
Plist.parse_xml `/usr/libexec/PlistBuddy -x -c print #{Shellwords.escape(plist.to_s)}`.force_encoding('UTF-8')
|
|
214
|
+
end # parse_plist
|
|
215
|
+
|
|
216
|
+
# TODO: Sill needed in Jamf API?
|
|
217
|
+
#
|
|
218
|
+
# Converts anything that responds to #to_s to a Time, or nil
|
|
219
|
+
#
|
|
220
|
+
# Return nil if the item is nil, 0 or an empty String.
|
|
221
|
+
#
|
|
222
|
+
# Otherwise the item converted to a string, and parsed with DateTime.parse.
|
|
223
|
+
# It is then examined to see if it has a UTC offset. If not, the local offset
|
|
224
|
+
# is applied, then the DateTime is converted to a Time.
|
|
225
|
+
#
|
|
226
|
+
# @param a_datetime [#to_s] The thing to convert to a time.
|
|
227
|
+
#
|
|
228
|
+
# @return [Time, nil] nil is returned if a_datetime is nil, 0 or an empty String.
|
|
229
|
+
#
|
|
230
|
+
def self.parse_time(a_datetime)
|
|
231
|
+
return nil if NIL_DATES.include? a_datetime
|
|
232
|
+
|
|
233
|
+
the_dt = DateTime.parse(a_datetime.to_s)
|
|
234
|
+
|
|
235
|
+
# The microseconds in DateTimes are stored as a fraction of a day.
|
|
236
|
+
# Convert them to an integer of microseconds
|
|
237
|
+
usec = (the_dt.sec_fraction * 60 * 60 * 24 * (10**6)).to_i
|
|
238
|
+
|
|
239
|
+
# if the UTC offset of the datetime is zero, make a new one with the correct local offset
|
|
240
|
+
# (which might also be zero if we happen to be in GMT)
|
|
241
|
+
the_dt = DateTime.new(the_dt.year, the_dt.month, the_dt.day, the_dt.hour, the_dt.min, the_dt.sec, Jamf::TIME_ZONE_OFFSET) if the_dt.offset.zero?
|
|
242
|
+
# now convert it to a Time and return it
|
|
243
|
+
Time.at the_dt.strftime('%s').to_i, usec
|
|
244
|
+
end # parse_time
|
|
245
|
+
|
|
246
|
+
# TODO: Sill needed in Jamf API?
|
|
247
|
+
#
|
|
248
|
+
# Converts JSS epoch (unix epoch + milliseconds) to a Ruby Time object
|
|
249
|
+
#
|
|
250
|
+
# @param epoch[String, Integer, nil]
|
|
251
|
+
#
|
|
252
|
+
# @return [Time, nil] nil is returned if epoch is nil, 0 or an empty String.
|
|
253
|
+
#
|
|
254
|
+
def self.epoch_to_time(epoch)
|
|
255
|
+
return nil if NIL_DATES.include? epoch
|
|
256
|
+
Time.at(epoch.to_i / 1000.0)
|
|
257
|
+
end # parse_date
|
|
258
|
+
|
|
259
|
+
# TODO: Move to APIObject
|
|
260
|
+
#
|
|
261
|
+
# Given a name, singular or plural, of a Jamf::APIObject subclass as a String
|
|
262
|
+
# or Symbol (e.g. :computer/'computers'), return the class itself
|
|
263
|
+
# (e.g. Jamf::Computer)
|
|
264
|
+
# The available names are the RSRC_LIST_KEY
|
|
265
|
+
# and RSRC_OBJECT_KEY values for each APIObject subclass.
|
|
266
|
+
#
|
|
267
|
+
# @seealso JSS.api_object_names
|
|
268
|
+
#
|
|
269
|
+
# @param name[String,Symbol] The name of a Jamf::APIObject subclass, singluar
|
|
270
|
+
# or plural
|
|
271
|
+
#
|
|
272
|
+
# @return [Class] The class
|
|
273
|
+
#
|
|
274
|
+
def self.api_object_class(name)
|
|
275
|
+
klass = api_object_names[name.downcase.to_sym]
|
|
276
|
+
raise Jamf::InvalidDataError, "Unknown API Object Class: #{name}" unless klass
|
|
277
|
+
klass
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# TODO: Move to APIObject
|
|
281
|
+
#
|
|
282
|
+
# APIObject subclasses have singular names, and are, of course
|
|
283
|
+
# capitalized, e.g. 'Computer'
|
|
284
|
+
# But we often want to refer to them in the plural, or lowercase,
|
|
285
|
+
# e.g. 'computers'
|
|
286
|
+
# This method returns a Hash of the RSRC_LIST_KEY (a plural symbol)
|
|
287
|
+
# and the RSRC_OBJECT_KEY (a singular symbol) of each APIObject
|
|
288
|
+
# subclass, keyed to the class itself, such that both :computer
|
|
289
|
+
# and :computers are keys for Jamf::Computer and both :policy and
|
|
290
|
+
# :policies are keys for Jamf::Policy, and so on.
|
|
291
|
+
#
|
|
292
|
+
# @return [Hash] APIObject subclass names to Classes
|
|
293
|
+
#
|
|
294
|
+
def self.api_object_names
|
|
295
|
+
return @api_object_names if @api_object_names
|
|
296
|
+
@api_object_names ||= {}
|
|
297
|
+
JSS.constants.each do |const|
|
|
298
|
+
klass = JSS.const_get const
|
|
299
|
+
next unless klass.is_a? Class
|
|
300
|
+
next unless klass.ancestors.include? Jamf::APIObject
|
|
301
|
+
@api_object_names[klass.const_get(:RSRC_LIST_KEY).to_sym] = klass if klass.constants.include? :RSRC_LIST_KEY
|
|
302
|
+
@api_object_names[klass.const_get(:RSRC_OBJECT_KEY).to_sym] = klass if klass.constants.include? :RSRC_OBJECT_KEY
|
|
303
|
+
end
|
|
304
|
+
@api_object_names
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# TODO: Update or remove for Jamf API
|
|
308
|
+
# Parse a JSS Version number into something comparable.
|
|
309
|
+
#
|
|
310
|
+
# This method returns a Hash with these keys:
|
|
311
|
+
# * :major => the major version, Integer
|
|
312
|
+
# * :minor => the minor version, Integor
|
|
313
|
+
# * :maint => the revision, Integer (also available as :patch and :revision)
|
|
314
|
+
# * :build => the revision, String
|
|
315
|
+
# * :version => a Gem::Version object built from :major, :minor, :revision
|
|
316
|
+
# which can be easily compared with other Gem::Version objects.
|
|
317
|
+
#
|
|
318
|
+
# NOTE: the :version value ignores build numbers, so comparisons
|
|
319
|
+
# only compare major.minor.maint
|
|
320
|
+
#
|
|
321
|
+
# @param version[String] a JSS version number from the API
|
|
322
|
+
#
|
|
323
|
+
# @return [Hash{Symbol => String, Gem::Version}] the parsed version data.
|
|
324
|
+
#
|
|
325
|
+
def self.parse_jss_version(version)
|
|
326
|
+
major, second_part, *_rest = version.split('.')
|
|
327
|
+
raise Jamf::InvalidDataError, 'JSS Versions must start with "x.x" where x is one or more digits' unless major =~ /\d$/ && second_part =~ /^\d/
|
|
328
|
+
|
|
329
|
+
release, build = version.split(/-/)
|
|
330
|
+
|
|
331
|
+
major, minor, revision = release.split '.'
|
|
332
|
+
minor ||= 0
|
|
333
|
+
revision ||= 0
|
|
334
|
+
|
|
335
|
+
{
|
|
336
|
+
major: major.to_i,
|
|
337
|
+
minor: minor.to_i,
|
|
338
|
+
revision: revision.to_i,
|
|
339
|
+
maint: revision.to_i,
|
|
340
|
+
patch: revision.to_i,
|
|
341
|
+
build: build,
|
|
342
|
+
version: Gem::Version.new("#{major}.#{minor}.#{revision}")
|
|
343
|
+
}
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# @return [Boolean] is this code running as root?
|
|
347
|
+
#
|
|
348
|
+
def self.superuser?
|
|
349
|
+
Process.euid.zero?
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Retrive one or all lines from whatever was piped to standard input.
|
|
353
|
+
#
|
|
354
|
+
# Standard input is read completely the first time this method is called
|
|
355
|
+
# and the lines are stored as an Array in the module var @stdin_lines
|
|
356
|
+
#
|
|
357
|
+
# @param line[Integer] which line of stdin is being retrieved.
|
|
358
|
+
# The default is zero (0) which returns all of stdin as a single string.
|
|
359
|
+
#
|
|
360
|
+
# @return [String, nil] the requested ling of stdin, or nil if it doesn't exist.
|
|
361
|
+
#
|
|
362
|
+
def self.stdin(line = 0)
|
|
363
|
+
@stdin_lines ||= ($stdin.tty? ? [] : $stdin.read.lines.map { |l| l.chomp("\n") })
|
|
364
|
+
|
|
365
|
+
return @stdin_lines.join("\n") if line <= 0
|
|
366
|
+
idx = line - 1
|
|
367
|
+
@stdin_lines[idx]
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# Prompt for a password in a terminal.
|
|
371
|
+
#
|
|
372
|
+
# @param message [String] the prompt message to display
|
|
373
|
+
#
|
|
374
|
+
# @return [String] the text typed by the user
|
|
375
|
+
#
|
|
376
|
+
def self.prompt_for_password(message)
|
|
377
|
+
begin
|
|
378
|
+
$stdin.reopen '/dev/tty' unless $stdin.tty?
|
|
379
|
+
$stderr.print "#{message} "
|
|
380
|
+
system '/bin/stty -echo'
|
|
381
|
+
pw = $stdin.gets.chomp("\n")
|
|
382
|
+
puts
|
|
383
|
+
ensure
|
|
384
|
+
system '/bin/stty echo'
|
|
385
|
+
end # begin
|
|
386
|
+
pw
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# Very handy!
|
|
390
|
+
# lifted from
|
|
391
|
+
# http://stackoverflow.com/questions/4136248/how-to-generate-a-human-readable-time-range-using-ruby-on-rails
|
|
392
|
+
#
|
|
393
|
+
def self.humanize_secs(secs)
|
|
394
|
+
[[60, :second], [60, :minute], [24, :hour], [7, :day], [52.179, :week], [1_000_000, :year]].map do |count, name|
|
|
395
|
+
next unless secs > 0
|
|
396
|
+
|
|
397
|
+
secs, n = secs.divmod(count)
|
|
398
|
+
n = n.to_i
|
|
399
|
+
"#{n} #{n == 1 ? name : (name.to_s + 's')}"
|
|
400
|
+
end.compact.reverse.join(' ')
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# un/set devmode mode.
|
|
404
|
+
# Useful when coding - methods can call JSS.devmode? and then
|
|
405
|
+
# e.g. spit out something instead of performing some action.
|
|
406
|
+
#
|
|
407
|
+
# @param [Symbol] Set devmode :on or :off
|
|
408
|
+
#
|
|
409
|
+
# @return [Boolean] The new state of devmode
|
|
410
|
+
#
|
|
411
|
+
def self.devmode(setting)
|
|
412
|
+
@devmode = setting == :on
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# is devmode currently on?
|
|
416
|
+
#
|
|
417
|
+
# @return [Boolean]
|
|
418
|
+
#
|
|
419
|
+
def self.devmode?
|
|
420
|
+
@devmode
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
end # module
|