ruby-jss 1.4.1 → 1.6.0
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/CHANGES.md +95 -0
- data/THANKS.md +3 -2
- data/lib/jamf.rb +18 -17
- data/lib/jamf/api/base_classes/collection_resource.rb +613 -0
- data/lib/jamf/api/{abstract_classes → base_classes}/json_object.rb +109 -101
- data/lib/jamf/api/{abstract_classes → base_classes}/prestage.rb +55 -30
- data/lib/jamf/api/{abstract_classes → base_classes}/resource.rb +10 -6
- data/lib/jamf/api/{abstract_classes → base_classes}/singleton_resource.rb +4 -3
- data/lib/jamf/api/connection.rb +13 -9
- data/lib/jamf/api/connection/api_error.rb +8 -8
- data/lib/jamf/api/connection/token.rb +16 -15
- data/lib/jamf/api/json_objects/device_enrollment_device.rb +14 -7
- data/lib/jamf/api/json_objects/{location.rb → device_enrollment_device_sync_state.rb} +27 -41
- data/lib/jamf/api/json_objects/device_enrollment_sync_status.rb +1 -1
- data/lib/jamf/api/json_objects/{attachment.rb → locale.rb} +14 -23
- data/lib/jamf/api/json_objects/md_prestage_name.rb +1 -1
- data/lib/jamf/api/json_objects/md_prestage_names.rb +2 -2
- data/lib/jamf/api/json_objects/md_prestage_skip_setup_items.rb +50 -1
- data/lib/jamf/api/json_objects/prestage_assignment.rb +2 -2
- data/lib/jamf/api/json_objects/prestage_location.rb +3 -3
- data/lib/jamf/api/json_objects/prestage_purchasing_data.rb +7 -7
- data/lib/jamf/api/json_objects/prestage_scope.rb +1 -1
- data/lib/jamf/api/{resources/collection_resources → json_objects}/time_zone.rb +9 -23
- data/lib/jamf/api/mixins/{abstract.rb → base_class.rb} +34 -16
- data/lib/jamf/api/mixins/bulk_deletable.rb +27 -6
- data/lib/jamf/api/mixins/change_log.rb +201 -51
- data/lib/jamf/api/{resources/collection_resources/computer.rb → mixins/filterable.rb} +19 -17
- data/lib/jamf/api/mixins/pageable.rb +208 -0
- data/lib/jamf/api/{json_objects/installed_application.rb → mixins/sortable.rb} +33 -33
- data/lib/jamf/api/resources/collection_resources/building.rb +16 -9
- data/lib/jamf/api/resources/collection_resources/category.rb +5 -4
- data/lib/jamf/api/resources/collection_resources/computer_prestage.rb +12 -5
- data/lib/jamf/api/resources/collection_resources/department.rb +0 -2
- data/lib/jamf/api/resources/collection_resources/device_enrollment.rb +10 -10
- data/lib/jamf/api/resources/collection_resources/inventory_preload_record.rb +11 -3
- data/lib/jamf/api/resources/collection_resources/mobile_device_prestage.rb +25 -23
- data/lib/jamf/api/resources/collection_resources/script.rb +61 -25
- data/lib/jamf/api/resources/singleton_resources/app_store_country_codes.rb +15 -5
- data/lib/jamf/api/resources/singleton_resources/locales.rb +155 -0
- data/lib/jamf/api/resources/singleton_resources/time_zones.rb +213 -0
- data/lib/jamf/client.rb +3 -3
- data/lib/jamf/client/management_action.rb +2 -3
- data/lib/jamf/composer.rb +2 -2
- data/lib/jamf/utility.rb +35 -7
- data/lib/jamf/validate.rb +63 -24
- data/lib/jamf/version.rb +1 -1
- data/lib/jss.rb +2 -2
- data/lib/jss/api_connection.rb +114 -406
- data/lib/jss/api_object.rb +3 -19
- data/lib/jss/api_object/categorizable.rb +1 -1
- data/lib/jss/api_object/computer.rb +13 -0
- data/lib/jss/api_object/configuration_profile.rb +61 -5
- data/lib/jss/api_object/directory_binding_type.rb +66 -60
- data/lib/jss/api_object/directory_binding_type/active_directory.rb +71 -34
- data/lib/jss/api_object/directory_binding_type/admitmac.rb +536 -467
- data/lib/jss/api_object/directory_binding_type/centrify.rb +21 -7
- data/lib/jss/api_object/directory_binding_type/open_directory.rb +4 -4
- data/lib/jss/api_object/distribution_point.rb +2 -2
- data/lib/jss/api_object/dock_item.rb +102 -96
- data/lib/jss/api_object/extendable.rb +1 -1
- data/lib/jss/api_object/group.rb +33 -2
- data/lib/jss/api_object/network_segment.rb +45 -13
- data/lib/jss/api_object/patch_source.rb +10 -9
- data/lib/jss/api_object/policy.rb +155 -25
- data/lib/jss/api_object/printer.rb +10 -4
- data/lib/jss/api_object/scopable.rb +10 -15
- data/lib/jss/api_object/scopable/scope.rb +31 -30
- data/lib/jss/api_object/script.rb +242 -352
- data/lib/jss/api_object/user.rb +1 -1
- data/lib/jss/client/management_action.rb +1 -2
- data/lib/jss/composer.rb +2 -2
- data/lib/jss/exceptions.rb +3 -0
- data/lib/jss/server.rb +15 -0
- data/lib/jss/utility.rb +213 -45
- data/lib/jss/version.rb +1 -1
- metadata +46 -64
- data/lib/jamf/api/abstract_classes/advanced_search.rb +0 -86
- data/lib/jamf/api/abstract_classes/collection_resource.rb +0 -433
- data/lib/jamf/api/abstract_classes/generic_reference.rb +0 -145
- data/lib/jamf/api/abstract_classes/prestage_skip_setup_items.rb +0 -126
- data/lib/jamf/api/json_objects/account_prefs.rb +0 -79
- data/lib/jamf/api/json_objects/android_details.rb +0 -139
- data/lib/jamf/api/json_objects/appletv_details.rb +0 -110
- data/lib/jamf/api/json_objects/cellular_network.rb +0 -151
- data/lib/jamf/api/json_objects/computer_prestage_skip_setup_items.rb +0 -67
- data/lib/jamf/api/json_objects/criterion.rb +0 -152
- data/lib/jamf/api/json_objects/extension_attribute_value.rb +0 -128
- data/lib/jamf/api/json_objects/installed_certificate.rb +0 -53
- data/lib/jamf/api/json_objects/installed_configuration_profile.rb +0 -67
- data/lib/jamf/api/json_objects/installed_ebook.rb +0 -58
- data/lib/jamf/api/json_objects/installed_provisioning_profile.rb +0 -59
- data/lib/jamf/api/json_objects/ios_details.rb +0 -244
- data/lib/jamf/api/json_objects/mobile_device_details.rb +0 -219
- data/lib/jamf/api/json_objects/mobile_device_security.rb +0 -101
- data/lib/jamf/api/json_objects/purchasing_data.rb +0 -125
- data/lib/jamf/api/mixins/locatable.rb +0 -124
- data/lib/jamf/api/mixins/referable.rb +0 -92
- data/lib/jamf/api/resources/collection_resources/account.rb +0 -163
- data/lib/jamf/api/resources/collection_resources/advanced_mobile_device_search.rb +0 -52
- data/lib/jamf/api/resources/collection_resources/advanced_user_search.rb +0 -52
- data/lib/jamf/api/resources/collection_resources/extension_attribute.rb +0 -45
- data/lib/jamf/api/resources/collection_resources/mobile_device.rb +0 -315
- data/lib/jamf/api/resources/collection_resources/site.rb +0 -77
- data/lib/jamf/api/resources/singleton_resources/authorization.rb +0 -88
- data/lib/jamf/api/resources/singleton_resources/client_checkin_settings.rb +0 -139
- data/lib/jamf/api/resources/singleton_resources/reenrollment_settings.rb +0 -95
|
@@ -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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
-
#
|
|
192
|
-
#
|
|
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
|
|
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
|
|
218
|
+
raise JSS::MissingDataError, "No such file: #{plist}" unless plist.file?
|
|
212
219
|
|
|
213
|
-
|
|
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/jamf/validate.rb
CHANGED
|
@@ -37,7 +37,7 @@ module Jamf
|
|
|
37
37
|
module Validate
|
|
38
38
|
|
|
39
39
|
# The regular expression that matches a valid MAC address.
|
|
40
|
-
MAC_ADDR_RE = /^[a-f0-9]{2}(:[a-f0-9]{2}){5}$/i
|
|
40
|
+
MAC_ADDR_RE = /^[a-f0-9]{2}(:[a-f0-9]{2}){5}$/i.freeze
|
|
41
41
|
|
|
42
42
|
# Validate the format and content of a MAC address
|
|
43
43
|
#
|
|
@@ -50,6 +50,7 @@ module Jamf
|
|
|
50
50
|
def self.mac_address(val, msg = nil)
|
|
51
51
|
msg ||= "Not a valid MAC address: '#{val}'"
|
|
52
52
|
raise Jamf::InvalidDataError, msg unless val =~ MAC_ADDR_RE
|
|
53
|
+
|
|
53
54
|
val
|
|
54
55
|
end
|
|
55
56
|
|
|
@@ -68,10 +69,11 @@ module Jamf
|
|
|
68
69
|
ok = false unless parts.size == 4
|
|
69
70
|
parts.each { |p| ok = false unless p.j_integer? && p.to_i < 256 && p.to_i >= 0 }
|
|
70
71
|
raise Jamf::InvalidDataError, msg unless ok
|
|
72
|
+
|
|
71
73
|
val
|
|
72
74
|
end
|
|
73
75
|
|
|
74
|
-
# Does a
|
|
76
|
+
# Does a given JSONObject class have a given JSON attribute?
|
|
75
77
|
#
|
|
76
78
|
# @param klass [<JSONObject] A class descended from JSONObject
|
|
77
79
|
#
|
|
@@ -83,9 +85,24 @@ module Jamf
|
|
|
83
85
|
raise "#{klass} is not a descendent of JSONObject" unless klass < Jamf::JSONObject
|
|
84
86
|
|
|
85
87
|
raise Jamf::NoSuchItemError, "No attribute #{attr_name} for class #{klass}" unless klass::OBJECT_MODEL.key? attrib
|
|
88
|
+
|
|
86
89
|
attr_name
|
|
87
90
|
end
|
|
88
91
|
|
|
92
|
+
# Does a value exist in a given enum array?
|
|
93
|
+
#
|
|
94
|
+
# @param klass [<JSONObject] A class descended from JSONObject
|
|
95
|
+
#
|
|
96
|
+
# @param attr_name [Symbol] The attribute to validate
|
|
97
|
+
#
|
|
98
|
+
# @return [Symbol] The valid attribute
|
|
99
|
+
#
|
|
100
|
+
def self.in_enum(val, enum)
|
|
101
|
+
raise Jamf::InvalidDataError, "Value must be one of: #{enum.join ', '}" unless enum.include? val
|
|
102
|
+
|
|
103
|
+
val
|
|
104
|
+
end
|
|
105
|
+
|
|
89
106
|
# Validate that a value doesn't already exist for a given identifier of
|
|
90
107
|
# a given CollectionResource class
|
|
91
108
|
#
|
|
@@ -116,14 +133,12 @@ module Jamf
|
|
|
116
133
|
raise Jamf::AlreadyExistsError, msg
|
|
117
134
|
end
|
|
118
135
|
|
|
136
|
+
TRUE_FALSE = [true, false].freeze
|
|
137
|
+
|
|
119
138
|
# Confirm that the given value is a boolean value, accepting
|
|
120
|
-
#
|
|
121
|
-
#
|
|
122
|
-
#
|
|
123
|
-
#
|
|
124
|
-
# Accepted False values: false, 'false', :false, 'no', :no
|
|
125
|
-
#
|
|
126
|
-
# all Strings and Symbols are case insensitive
|
|
139
|
+
# strings and symbols and returning real booleans as needed
|
|
140
|
+
# Accepts: true, false, 'true', 'false', 'yes', 'no', 't','f', 'y', or 'n'
|
|
141
|
+
# as strings or symbols, case insensitive
|
|
127
142
|
#
|
|
128
143
|
# @param val [Boolean,String,Symbol] The value to validate
|
|
129
144
|
#
|
|
@@ -131,14 +146,36 @@ module Jamf
|
|
|
131
146
|
#
|
|
132
147
|
# @return [Boolean] the valid boolean
|
|
133
148
|
#
|
|
134
|
-
def self.boolean(val, msg =
|
|
135
|
-
|
|
136
|
-
return true if val.to_s =~ /^(
|
|
137
|
-
return false if val.to_s =~ /^(
|
|
149
|
+
def self.boolean(val, msg = 'Value must be true or false, or equivalent string or symbol')
|
|
150
|
+
return val if TRUE_FALSE.include? val
|
|
151
|
+
return true if val.to_s =~ /^(t(rue)?|y(es)?)$/i
|
|
152
|
+
return false if val.to_s =~ /^(f(alse)?|no?)$/i
|
|
138
153
|
|
|
139
154
|
raise Jamf::InvalidDataError, msg
|
|
140
155
|
end
|
|
141
156
|
|
|
157
|
+
# Confirm that a value provided is an integer or a string version
|
|
158
|
+
# of an integer, and return the string version
|
|
159
|
+
#
|
|
160
|
+
# The JPAPI specs say that all IDs are integers in strings
|
|
161
|
+
# tho, the endpoints are still implementing that in different versions.
|
|
162
|
+
#
|
|
163
|
+
# @param val[Object] the value to validate
|
|
164
|
+
#
|
|
165
|
+
# @param msg[String] A custom error message when the value is invalid
|
|
166
|
+
#
|
|
167
|
+
# @return [String] the valid integer-in-a-string
|
|
168
|
+
#
|
|
169
|
+
def self.j_id(val, msg = 'Value must be an Integer or an Integer in a String, e.g. "42"')
|
|
170
|
+
case val
|
|
171
|
+
when Integer
|
|
172
|
+
return val.to_s
|
|
173
|
+
when String
|
|
174
|
+
return val if val.j_integer?
|
|
175
|
+
end
|
|
176
|
+
raise Jamf::InvalidDataError, msg
|
|
177
|
+
end
|
|
178
|
+
|
|
142
179
|
# Confirm that a value is an Integer or a String representation of an
|
|
143
180
|
# Integer. Return the integer, or raise an error
|
|
144
181
|
#
|
|
@@ -148,10 +185,10 @@ module Jamf
|
|
|
148
185
|
#
|
|
149
186
|
# @return [Integer] the valid integer
|
|
150
187
|
#
|
|
151
|
-
def self.integer(val, msg =
|
|
152
|
-
msg ||= 'Value must be an Integer'
|
|
188
|
+
def self.integer(val, msg = 'Value must be an Integer')
|
|
153
189
|
val = val.to_i if val.is_a?(String) && val.j_integer?
|
|
154
190
|
raise Jamf::InvalidDataError, msg unless val.is_a? Integer
|
|
191
|
+
|
|
155
192
|
val
|
|
156
193
|
end
|
|
157
194
|
|
|
@@ -164,10 +201,10 @@ module Jamf
|
|
|
164
201
|
#
|
|
165
202
|
# @return [Float] the valid float
|
|
166
203
|
#
|
|
167
|
-
def self.float(val, msg =
|
|
168
|
-
msg ||= 'Value must be a Floating Point number'
|
|
204
|
+
def self.float(val, msg = 'Value must be a Floating Point number')
|
|
169
205
|
val = val.to_f if val.is_a?(String) && val.j_float?
|
|
170
|
-
raise Jamf::InvalidDataError, msg unless val.is_a?
|
|
206
|
+
raise Jamf::InvalidDataError, msg unless val.is_a? Float
|
|
207
|
+
|
|
171
208
|
val
|
|
172
209
|
end
|
|
173
210
|
|
|
@@ -180,11 +217,12 @@ module Jamf
|
|
|
180
217
|
#
|
|
181
218
|
# @return [String] the valid String
|
|
182
219
|
#
|
|
183
|
-
def self.string(val, msg =
|
|
184
|
-
msg ||= 'Value must be a String'
|
|
220
|
+
def self.string(val, msg = 'Value must be a String')
|
|
185
221
|
return Jamf::BLANK if val.nil?
|
|
222
|
+
|
|
186
223
|
val = val.to_s if val.is_a? Symbol
|
|
187
224
|
raise Jamf::InvalidDataError, msg unless val.is_a? String
|
|
225
|
+
|
|
188
226
|
val
|
|
189
227
|
end
|
|
190
228
|
|
|
@@ -197,10 +235,10 @@ module Jamf
|
|
|
197
235
|
#
|
|
198
236
|
# @return [String] the valid non-empty string
|
|
199
237
|
#
|
|
200
|
-
def self.non_empty_string(val, msg =
|
|
201
|
-
msg ||= 'value must be a non-empty String'
|
|
238
|
+
def self.non_empty_string(val, msg = 'value must be a non-empty String')
|
|
202
239
|
val = val.to_s if val.is_a? Symbol
|
|
203
240
|
raise Jamf::InvalidDataError, msg unless val.is_a?(String) && !val.empty?
|
|
241
|
+
|
|
204
242
|
val
|
|
205
243
|
end
|
|
206
244
|
|
|
@@ -214,11 +252,12 @@ module Jamf
|
|
|
214
252
|
#
|
|
215
253
|
# @return [String] the validated string
|
|
216
254
|
#
|
|
217
|
-
def self.script_contents(val, msg =
|
|
218
|
-
msg ||= "value must be a String starting with '#!'"
|
|
255
|
+
def self.script_contents(val, msg = "value must be a String starting with '#!'")
|
|
219
256
|
raise Jamf::InvalidDataError, msg unless val.is_a?(String) && val.start_with?(SCRIPT_SHEBANG)
|
|
257
|
+
|
|
220
258
|
val
|
|
221
259
|
end
|
|
260
|
+
|
|
222
261
|
end # module validate
|
|
223
262
|
|
|
224
263
|
end # module JSS
|
data/lib/jamf/version.rb
CHANGED
data/lib/jss.rb
CHANGED
data/lib/jss/api_connection.rb
CHANGED
|
@@ -25,18 +25,6 @@
|
|
|
25
25
|
###
|
|
26
26
|
module JSS
|
|
27
27
|
|
|
28
|
-
# Constants
|
|
29
|
-
#####################################
|
|
30
|
-
|
|
31
|
-
# Module Variables
|
|
32
|
-
#####################################
|
|
33
|
-
|
|
34
|
-
# Module Methods
|
|
35
|
-
#####################################
|
|
36
|
-
|
|
37
|
-
# Classes
|
|
38
|
-
#####################################
|
|
39
|
-
|
|
40
28
|
# Instances of this class represent a REST connection to a JSS API.
|
|
41
29
|
#
|
|
42
30
|
# For most cases, a single connection to a single JSS is all you need, and
|
|
@@ -227,42 +215,6 @@ module JSS
|
|
|
227
215
|
# # the variable 'prod2_victim_md' now contains a JSS::MobileDevice queried
|
|
228
216
|
# # through the connection 'production_api2'.
|
|
229
217
|
#
|
|
230
|
-
# == Using the APIConnection itself to make API calls.
|
|
231
|
-
#
|
|
232
|
-
# Rather than passing an APIConnection into another method, you can call
|
|
233
|
-
# similar methods on the connection itself. For example, these two calls
|
|
234
|
-
# have the same result as the two examples above:
|
|
235
|
-
#
|
|
236
|
-
# prod2_computer_sns = production_api2.all :Computer, only: :serial_numbers
|
|
237
|
-
# prod2_victim_md = production_api2.fetch :MobileDevice, id: 832
|
|
238
|
-
#
|
|
239
|
-
# Here are the API calls you can make directly from an APIConnection object.
|
|
240
|
-
# They behave practically identically to the same methods in the APIObject
|
|
241
|
-
# subclasses, since they just call those methods, passing themselves in as the
|
|
242
|
-
# APIConnection to use.
|
|
243
|
-
#
|
|
244
|
-
# - {#all} The 'list' methods of the various APIObject classes. Use the 'only:'
|
|
245
|
-
# parameter to specify one of the sub-list-methods, like #all_ids or
|
|
246
|
-
# #all_laptops, e.g. `my_connection.all :computers, only: :id`
|
|
247
|
-
# - {#map_all_ids} the equivalent of #map_all_ids_to in the APIObject classes
|
|
248
|
-
# - {#valid_id} given a class and an identifier (like macaddress or udid)
|
|
249
|
-
# return a valid id or nil
|
|
250
|
-
# - {#exist?} given a class and an identifier (like macaddress or udid) does
|
|
251
|
-
# the identifier exist for the class in the JSS
|
|
252
|
-
# - {#match} list items in the JSS matching a query
|
|
253
|
-
# (if the object is {Matchable})
|
|
254
|
-
# - {#fetch} retrieve an object from the JSS
|
|
255
|
-
# - {#make} instantiate an object to be created in the JSS
|
|
256
|
-
# - {#computer_checkin_settings} same as {Computer.checkin_settings}
|
|
257
|
-
# - {#computer_inventory_collection_settings} same as {Computer.inventory_collection_settings}
|
|
258
|
-
# - {#computer_application_usage} same as {Computer.application_usage}
|
|
259
|
-
# - {#computer_management_data} same as {Computer.management_data}
|
|
260
|
-
# - {#master_distribution_point} same as {DistributionPoint.master_distribution_point}
|
|
261
|
-
# - {#my_distribution_point} same as {DistributionPoint.my_distribution_point}
|
|
262
|
-
# - {#network_ranges} same as {NetworkSegment.network_ranges}
|
|
263
|
-
# - {#network_segments_for_ip} same as {NetworkSegment.segments_for_ip}
|
|
264
|
-
# - {#my_network_segments} same as {NetworkSegment.my_network_segments}
|
|
265
|
-
#
|
|
266
218
|
# == Low-level use of APIConnection instances.
|
|
267
219
|
#
|
|
268
220
|
# For most cases, using APIConnection instances as mentioned above
|
|
@@ -271,7 +223,7 @@ module JSS
|
|
|
271
223
|
# {#get_rsrc}, {#put_rsrc}, {#post_rsrc}, & {#delete_rsrc}
|
|
272
224
|
# documented below.
|
|
273
225
|
#
|
|
274
|
-
# For even lower-level work, you can access the underlying
|
|
226
|
+
# For even lower-level work, you can access the underlying Faraday::Connection
|
|
275
227
|
# inside the APIConnection via the connection's {#cnx} attribute.
|
|
276
228
|
#
|
|
277
229
|
# APIConnection instances also have a {#server} attribute which contains an
|
|
@@ -330,6 +282,12 @@ module JSS
|
|
|
330
282
|
# values for the format param of get_rsrc
|
|
331
283
|
GET_FORMATS = %i[json xml].freeze
|
|
332
284
|
|
|
285
|
+
HTTP_ACCEPT_HEADER = 'Accept'.freeze
|
|
286
|
+
HTTP_CONTENT_TYPE_HEADER = 'Content-Type'.freeze
|
|
287
|
+
|
|
288
|
+
MIME_JSON = 'application/json'.freeze
|
|
289
|
+
MIME_XML = 'application/xml'.freeze
|
|
290
|
+
|
|
333
291
|
# Attributes
|
|
334
292
|
#####################################
|
|
335
293
|
|
|
@@ -337,7 +295,7 @@ module JSS
|
|
|
337
295
|
attr_reader :user
|
|
338
296
|
alias jss_user user
|
|
339
297
|
|
|
340
|
-
# @return [
|
|
298
|
+
# @return [Faraday::Connection] the underlying connection resource
|
|
341
299
|
attr_reader :cnx
|
|
342
300
|
|
|
343
301
|
# @return [Boolean] are we connected right now?
|
|
@@ -359,7 +317,7 @@ module JSS
|
|
|
359
317
|
# @return [String] the protocol being used: http or https
|
|
360
318
|
attr_reader :protocol
|
|
361
319
|
|
|
362
|
-
# @return [
|
|
320
|
+
# @return [Faraday::Response] The response from the most recent API call
|
|
363
321
|
attr_reader :last_http_response
|
|
364
322
|
|
|
365
323
|
# @return [String] The base URL to to the current REST API
|
|
@@ -428,6 +386,7 @@ module JSS
|
|
|
428
386
|
@name = args.delete :name
|
|
429
387
|
@name ||= :unknown
|
|
430
388
|
@connected = false
|
|
389
|
+
@object_list_cache = {}
|
|
431
390
|
connect args unless args.empty?
|
|
432
391
|
end # init
|
|
433
392
|
|
|
@@ -453,8 +412,6 @@ module JSS
|
|
|
453
412
|
# @option args :use_ssl[Boolean] should the connection be made over SSL? Defaults to true.
|
|
454
413
|
#
|
|
455
414
|
# @option args :verify_cert[Boolean] should HTTPS SSL certificates be verified. Defaults to true.
|
|
456
|
-
# If your connection raises RestClient::SSLCertificateNotVerified, and you don't care about the
|
|
457
|
-
# validity of the SSL cert. just set this explicitly to false.
|
|
458
415
|
#
|
|
459
416
|
# @option args :user[String] a JSS user who has API privs, required if not defined in JSS::CONFIG
|
|
460
417
|
#
|
|
@@ -476,6 +433,8 @@ module JSS
|
|
|
476
433
|
|
|
477
434
|
args[:no_port_specified] = args[:port].to_s.empty?
|
|
478
435
|
args = apply_connection_defaults args
|
|
436
|
+
@timeout = args[:timeout]
|
|
437
|
+
@open_timeout = args[:open_timeout]
|
|
479
438
|
|
|
480
439
|
# ensure an integer
|
|
481
440
|
args[:port] &&= args[:port].to_i
|
|
@@ -494,7 +453,7 @@ module JSS
|
|
|
494
453
|
args[:password] = acquire_password args
|
|
495
454
|
|
|
496
455
|
# heres our connection
|
|
497
|
-
@cnx =
|
|
456
|
+
@cnx = create_connection args[:password]
|
|
498
457
|
|
|
499
458
|
verify_server_version
|
|
500
459
|
|
|
@@ -543,8 +502,7 @@ module JSS
|
|
|
543
502
|
@connected = false
|
|
544
503
|
end # disconnect
|
|
545
504
|
|
|
546
|
-
# Get
|
|
547
|
-
#
|
|
505
|
+
# Get a JSS resource
|
|
548
506
|
# The first argument is the resource to get (the part of the API url
|
|
549
507
|
# after the 'JSSResource/' ) The resource must be properly URL escaped
|
|
550
508
|
# beforehand. Note: URL.encode is deprecated, use CGI.escape
|
|
@@ -572,14 +530,19 @@ module JSS
|
|
|
572
530
|
validate_connected
|
|
573
531
|
raise JSS::InvalidDataError, 'format must be :json or :xml' unless GET_FORMATS.include? format
|
|
574
532
|
|
|
575
|
-
|
|
576
|
-
@
|
|
577
|
-
|
|
533
|
+
@last_http_response =
|
|
534
|
+
@cnx.get(rsrc) do |req|
|
|
535
|
+
req.headers[HTTP_ACCEPT_HEADER] = format == :json ? MIME_JSON : MIME_XML
|
|
536
|
+
end
|
|
578
537
|
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
538
|
+
unless @last_http_response.success?
|
|
539
|
+
handle_http_error
|
|
540
|
+
return
|
|
582
541
|
end
|
|
542
|
+
|
|
543
|
+
return JSON.parse(@last_http_response.body, symbolize_names: true) if format == :json && !raw_json
|
|
544
|
+
|
|
545
|
+
@last_http_response.body
|
|
583
546
|
end
|
|
584
547
|
|
|
585
548
|
# Update an existing JSS resource
|
|
@@ -597,10 +560,18 @@ module JSS
|
|
|
597
560
|
xml.gsub!(/\r/, ' ')
|
|
598
561
|
|
|
599
562
|
# send the data
|
|
600
|
-
@last_http_response =
|
|
563
|
+
@last_http_response =
|
|
564
|
+
@cnx.put(rsrc) do |req|
|
|
565
|
+
req.headers[HTTP_CONTENT_TYPE_HEADER] = MIME_XML
|
|
566
|
+
req.headers[HTTP_ACCEPT_HEADER] = MIME_XML
|
|
567
|
+
req.body = xml
|
|
568
|
+
end
|
|
569
|
+
unless @last_http_response.success?
|
|
570
|
+
handle_http_error
|
|
571
|
+
return
|
|
572
|
+
end
|
|
573
|
+
|
|
601
574
|
@last_http_response.body
|
|
602
|
-
rescue RestClient::ExceptionWithResponse => e
|
|
603
|
-
handle_http_error e
|
|
604
575
|
end
|
|
605
576
|
|
|
606
577
|
# Create a new JSS resource
|
|
@@ -611,17 +582,24 @@ module JSS
|
|
|
611
582
|
#
|
|
612
583
|
# @return [String] the xml response from the server.
|
|
613
584
|
#
|
|
614
|
-
def post_rsrc(rsrc, xml
|
|
585
|
+
def post_rsrc(rsrc, xml)
|
|
615
586
|
validate_connected
|
|
616
587
|
|
|
617
588
|
# convert CRs & to
|
|
618
|
-
xml
|
|
589
|
+
xml&.gsub!(/\r/, ' ')
|
|
619
590
|
|
|
620
591
|
# send the data
|
|
621
|
-
@last_http_response =
|
|
592
|
+
@last_http_response =
|
|
593
|
+
@cnx.post(rsrc) do |req|
|
|
594
|
+
req.headers[HTTP_CONTENT_TYPE_HEADER] = MIME_XML
|
|
595
|
+
req.headers[HTTP_ACCEPT_HEADER] = MIME_XML
|
|
596
|
+
req.body = xml
|
|
597
|
+
end
|
|
598
|
+
unless @last_http_response.success?
|
|
599
|
+
handle_http_error
|
|
600
|
+
return
|
|
601
|
+
end
|
|
622
602
|
@last_http_response.body
|
|
623
|
-
rescue RestClient::ExceptionWithResponse => e
|
|
624
|
-
handle_http_error e
|
|
625
603
|
end # post_rsrc
|
|
626
604
|
|
|
627
605
|
# Delete a resource from the JSS
|
|
@@ -630,18 +608,23 @@ module JSS
|
|
|
630
608
|
#
|
|
631
609
|
# @return [String] the xml response from the server.
|
|
632
610
|
#
|
|
633
|
-
def delete_rsrc(rsrc
|
|
611
|
+
def delete_rsrc(rsrc)
|
|
634
612
|
validate_connected
|
|
635
613
|
raise MissingDataError, 'Missing :rsrc' if rsrc.nil?
|
|
636
614
|
|
|
637
|
-
# payload?
|
|
638
|
-
return delete_with_payload rsrc, xml if xml
|
|
639
|
-
|
|
640
615
|
# delete the resource
|
|
641
|
-
@last_http_response =
|
|
616
|
+
@last_http_response =
|
|
617
|
+
@cnx.delete(rsrc) do |req|
|
|
618
|
+
req.headers[HTTP_CONTENT_TYPE_HEADER] = MIME_XML
|
|
619
|
+
req.headers[HTTP_ACCEPT_HEADER] = MIME_XML
|
|
620
|
+
end
|
|
621
|
+
|
|
622
|
+
unless @last_http_response.success?
|
|
623
|
+
handle_http_error
|
|
624
|
+
return
|
|
625
|
+
end
|
|
626
|
+
|
|
642
627
|
@last_http_response.body
|
|
643
|
-
rescue RestClient::ExceptionWithResponse => e
|
|
644
|
-
handle_http_error e
|
|
645
628
|
end # delete_rsrc
|
|
646
629
|
|
|
647
630
|
# Test that a given hostname & port is a JSS API server
|
|
@@ -657,25 +640,8 @@ module JSS
|
|
|
657
640
|
# ssl_options like :OP_NO_SSLv2 and :OP_NO_SSLv3 will take time to figure out..
|
|
658
641
|
return true if `/usr/bin/curl -s 'https://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
|
|
659
642
|
return true if `/usr/bin/curl -s 'http://#{server}:#{port}/#{TEST_PATH}'`.include? TEST_CONTENT
|
|
660
|
-
false
|
|
661
643
|
|
|
662
|
-
|
|
663
|
-
# # NOTE: doesn't work if we can't disallow SSLv3 or force TLSv1
|
|
664
|
-
# # See cheat above.
|
|
665
|
-
# begin
|
|
666
|
-
# return true if open("https://#{server}:#{port}/#{TEST_PATH}", ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE).read.include? TEST_CONTENT
|
|
667
|
-
#
|
|
668
|
-
# rescue
|
|
669
|
-
# # then regular http
|
|
670
|
-
# begin
|
|
671
|
-
# return true if open("http://#{server}:#{port}/#{TEST_PATH}").read.include? TEST_CONTENT
|
|
672
|
-
# rescue
|
|
673
|
-
# # any errors = no API
|
|
674
|
-
# return false
|
|
675
|
-
# end # begin
|
|
676
|
-
# end # begin
|
|
677
|
-
# # if we're here, no API
|
|
678
|
-
# false
|
|
644
|
+
false
|
|
679
645
|
end
|
|
680
646
|
|
|
681
647
|
# The server to which we are connected, or will
|
|
@@ -686,267 +652,13 @@ module JSS
|
|
|
686
652
|
#
|
|
687
653
|
def hostname
|
|
688
654
|
return @server_host if @server_host
|
|
655
|
+
|
|
689
656
|
srvr = JSS::CONFIG.api_server_name
|
|
690
657
|
srvr ||= JSS::Client.jss_server
|
|
691
658
|
srvr
|
|
692
659
|
end
|
|
693
660
|
alias host hostname
|
|
694
661
|
|
|
695
|
-
#################
|
|
696
|
-
|
|
697
|
-
# Call one of the 'all*' methods on a JSS::APIObject subclass
|
|
698
|
-
# using this APIConnection.
|
|
699
|
-
#
|
|
700
|
-
#
|
|
701
|
-
# @deprecated please use the .all class method of the desired class
|
|
702
|
-
#
|
|
703
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
704
|
-
# see {JSS.api_object_class}
|
|
705
|
-
#
|
|
706
|
-
# @param refresh[Boolean] Should the data be re-read from the API?
|
|
707
|
-
#
|
|
708
|
-
# @param only[String,Symbol] Limit the output to subset or data. All
|
|
709
|
-
# APIObject subclasses can take :ids or :names, which calls the .all_ids
|
|
710
|
-
# and .all_names methods. Some subclasses can take other options, e.g.
|
|
711
|
-
# MobileDevice can take :udids
|
|
712
|
-
#
|
|
713
|
-
# @return [Array] The list of items for the class
|
|
714
|
-
#
|
|
715
|
-
def all(class_name, refresh = false, only: nil)
|
|
716
|
-
the_class = JSS.api_object_class(class_name)
|
|
717
|
-
list_method = only ? :"all_#{only}" : :all
|
|
718
|
-
|
|
719
|
-
raise ArgumentError, "Unknown identifier: #{only} for #{the_class}" unless
|
|
720
|
-
the_class.respond_to? list_method
|
|
721
|
-
|
|
722
|
-
the_class.send list_method, refresh, api: self
|
|
723
|
-
end
|
|
724
|
-
|
|
725
|
-
# Call the 'map_all_ids_to' method on a JSS::APIObject subclass
|
|
726
|
-
# using this APIConnection.
|
|
727
|
-
#
|
|
728
|
-
# @deprecated please use the .map_all_ids_to class method of the desired class
|
|
729
|
-
#
|
|
730
|
-
#
|
|
731
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
732
|
-
# see {JSS.api_object_class}
|
|
733
|
-
#
|
|
734
|
-
# @param refresh[Boolean] Should the data be re-read from the API?
|
|
735
|
-
#
|
|
736
|
-
# @param to[String,Symbol] the value to which the ids should be mapped
|
|
737
|
-
#
|
|
738
|
-
# @return [Hash] The ids for the class keyed to the requested identifier
|
|
739
|
-
#
|
|
740
|
-
def map_all_ids(class_name, refresh = false, to: nil)
|
|
741
|
-
raise "'to:' value must be provided for mapping ids." unless to
|
|
742
|
-
the_class = JSS.api_object_class(class_name)
|
|
743
|
-
the_class.map_all_ids_to to, refresh, api: self
|
|
744
|
-
end
|
|
745
|
-
|
|
746
|
-
# Call the 'valid_id' method on a JSS::APIObject subclass
|
|
747
|
-
# using this APIConnection. See {JSS::APIObject.valid_id}
|
|
748
|
-
#
|
|
749
|
-
# @deprecated please use the .valid_id class method of the desired class
|
|
750
|
-
#
|
|
751
|
-
#
|
|
752
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass,
|
|
753
|
-
# see {JSS.api_object_class}
|
|
754
|
-
#
|
|
755
|
-
# @param identifier[String,Symbol] the value to which the ids should be mapped
|
|
756
|
-
#
|
|
757
|
-
# @param refresh[Boolean] Should the data be re-read from the API?
|
|
758
|
-
#
|
|
759
|
-
# @return [Integer, nil] the id of the matching object of the class,
|
|
760
|
-
# or nil if there isn't one
|
|
761
|
-
#
|
|
762
|
-
def valid_id(class_name, identifier, refresh = true)
|
|
763
|
-
the_class = JSS.api_object_class(class_name)
|
|
764
|
-
the_class.valid_id identifier, refresh, api: self
|
|
765
|
-
end
|
|
766
|
-
|
|
767
|
-
# Call the 'exist?' method on a JSS::APIObject subclass
|
|
768
|
-
# using this APIConnection. See {JSS::APIObject.exist?}
|
|
769
|
-
#
|
|
770
|
-
# @deprecated please use the .exist class method of the desired class
|
|
771
|
-
#
|
|
772
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
773
|
-
# see {JSS.api_object_class}
|
|
774
|
-
#
|
|
775
|
-
# @param identifier[String,Symbol] the value to which the ids should be mapped
|
|
776
|
-
#
|
|
777
|
-
# @param refresh[Boolean] Should the data be re-read from the API?
|
|
778
|
-
#
|
|
779
|
-
# @return [Boolean] Is there an object of this class in the JSS matching
|
|
780
|
-
# this indentifier?
|
|
781
|
-
#
|
|
782
|
-
def exist?(class_name, identifier, refresh = false)
|
|
783
|
-
!valid_id(class_name, identifier, refresh).nil?
|
|
784
|
-
end
|
|
785
|
-
|
|
786
|
-
# Call {Matchable.match} for the given class.
|
|
787
|
-
#
|
|
788
|
-
# See {Matchable.match}
|
|
789
|
-
#
|
|
790
|
-
# @deprecated Please use the .match class method of the desired class
|
|
791
|
-
#
|
|
792
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
793
|
-
# see {JSS.api_object_class}
|
|
794
|
-
#
|
|
795
|
-
# @return (see Matchable.match)
|
|
796
|
-
#
|
|
797
|
-
def match(class_name, term)
|
|
798
|
-
the_class = JSS.api_object_class(class_name)
|
|
799
|
-
raise JSS::UnsupportedError, "Class #{the_class} is not matchable" unless the_class.respond_to? :match
|
|
800
|
-
the_class.match term, api: self
|
|
801
|
-
end
|
|
802
|
-
|
|
803
|
-
# Retrieve an object of a given class from the API
|
|
804
|
-
# See {APIObject.fetch}
|
|
805
|
-
#
|
|
806
|
-
# @deprecated Please use the .fetch class method of the desired class
|
|
807
|
-
#
|
|
808
|
-
#
|
|
809
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
810
|
-
# see {JSS.api_object_class}
|
|
811
|
-
#
|
|
812
|
-
# @return [APIObject] The ruby-instance of the object.
|
|
813
|
-
#
|
|
814
|
-
def fetch(class_name, arg)
|
|
815
|
-
the_class = JSS.api_object_class(class_name)
|
|
816
|
-
the_class.fetch arg, api: self
|
|
817
|
-
end
|
|
818
|
-
|
|
819
|
-
# Make a ruby instance of a not-yet-existing APIObject
|
|
820
|
-
# of the given class
|
|
821
|
-
# See {APIObject.make}
|
|
822
|
-
#
|
|
823
|
-
# @deprecated Please use the .make class method of the desired class
|
|
824
|
-
#
|
|
825
|
-
# @param class_name[String,Symbol] The name of a JSS::APIObject subclass
|
|
826
|
-
# see {JSS.api_object_class}
|
|
827
|
-
#
|
|
828
|
-
# @return [APIObject] The un-created ruby-instance of the object.
|
|
829
|
-
#
|
|
830
|
-
def make(class_name, **args)
|
|
831
|
-
the_class = JSS.api_object_class(class_name)
|
|
832
|
-
args[:api] = self
|
|
833
|
-
the_class.make args
|
|
834
|
-
end
|
|
835
|
-
|
|
836
|
-
# Call {JSS::Computer.checkin_settings} q.v., passing this API
|
|
837
|
-
# connection
|
|
838
|
-
# @deprecated Please use JSS::Computer.checkin_settings
|
|
839
|
-
#
|
|
840
|
-
def computer_checkin_settings
|
|
841
|
-
JSS::Computer.checkin_settings api: self
|
|
842
|
-
end
|
|
843
|
-
|
|
844
|
-
# Call {JSS::Computer.inventory_collection_settings} q.v., passing this API
|
|
845
|
-
# connection
|
|
846
|
-
# @deprecated Please use JSS::Computer.inventory_collection_settings
|
|
847
|
-
#
|
|
848
|
-
def computer_inventory_collection_settings
|
|
849
|
-
JSS::Computer.inventory_collection_settings api: self
|
|
850
|
-
end
|
|
851
|
-
|
|
852
|
-
# Call {JSS::Computer.application_usage} q.v., passing this API
|
|
853
|
-
# connection
|
|
854
|
-
# @deprecated Please use JSS::Computer.application_usage
|
|
855
|
-
#
|
|
856
|
-
def computer_application_usage(ident, start_date, end_date = nil)
|
|
857
|
-
JSS::Computer.application_usage ident, start_date, end_date, api: self
|
|
858
|
-
end
|
|
859
|
-
|
|
860
|
-
# Call {JSS::Computer.management_data} q.v., passing this API
|
|
861
|
-
# connection
|
|
862
|
-
#
|
|
863
|
-
# @deprecated Please use JSS::Computer.management_data
|
|
864
|
-
#
|
|
865
|
-
def computer_management_data(ident, subset: nil, only: nil)
|
|
866
|
-
JSS::Computer.management_data ident, subset: subset, only: only, api: self
|
|
867
|
-
end
|
|
868
|
-
|
|
869
|
-
# Call {JSS::Computer.history} q.v., passing this API
|
|
870
|
-
# connection
|
|
871
|
-
#
|
|
872
|
-
# @deprecated Please use JSS::Computer.management_history or its
|
|
873
|
-
# convenience methods. @see JSS::ManagementHistory
|
|
874
|
-
#
|
|
875
|
-
def computer_history(ident, subset: nil)
|
|
876
|
-
JSS::Computer.history ident, subset, api: self
|
|
877
|
-
end
|
|
878
|
-
|
|
879
|
-
# Call {JSS::Computer.send_mdm_command} q.v., passing this API
|
|
880
|
-
# connection
|
|
881
|
-
#
|
|
882
|
-
# @deprecated Please use JSS::Computer.send_mdm_command or its
|
|
883
|
-
# convenience methods. @see JSS::MDM
|
|
884
|
-
#
|
|
885
|
-
def send_computer_mdm_command(targets, command, passcode = nil)
|
|
886
|
-
opts = passcode ? { passcode: passcode } : {}
|
|
887
|
-
JSS::Computer.send_mdm_command targets, command, opts: opts, api: self
|
|
888
|
-
end
|
|
889
|
-
|
|
890
|
-
# Get the DistributionPoint instance for the master
|
|
891
|
-
# distribution point in the JSS. If there's only one
|
|
892
|
-
# in the JSS, return it even if not marked as master.
|
|
893
|
-
#
|
|
894
|
-
# @param refresh[Boolean] re-read from the API?
|
|
895
|
-
#
|
|
896
|
-
# @return [JSS::DistributionPoint]
|
|
897
|
-
#
|
|
898
|
-
def master_distribution_point(refresh = false)
|
|
899
|
-
JSS::DistributionPoint.master_distribution_point refresh, api: self
|
|
900
|
-
end
|
|
901
|
-
|
|
902
|
-
# Get the DistributionPoint instance for the machine running
|
|
903
|
-
# this code, based on its IP address. If none is defined for this IP address,
|
|
904
|
-
# use the result of master_distribution_point
|
|
905
|
-
#
|
|
906
|
-
# @param refresh[Boolean] should the distribution point be re-queried?
|
|
907
|
-
#
|
|
908
|
-
# @return [JSS::DistributionPoint]
|
|
909
|
-
#
|
|
910
|
-
def my_distribution_point(refresh = false)
|
|
911
|
-
JSS::DistributionPoint.my_distribution_point refresh, api: self
|
|
912
|
-
end
|
|
913
|
-
|
|
914
|
-
# @deprecated
|
|
915
|
-
#
|
|
916
|
-
# @see {JSS::NetworkSegment.network_ranges}
|
|
917
|
-
#
|
|
918
|
-
def network_ranges(refresh = false)
|
|
919
|
-
JSS::NetworkSegment.network_ranges refresh, api: self
|
|
920
|
-
end # def network_segments
|
|
921
|
-
|
|
922
|
-
# @deprecated
|
|
923
|
-
#
|
|
924
|
-
# @see {JSS::NetworkSegment.network_segments_for_ip}
|
|
925
|
-
#
|
|
926
|
-
def network_segments_for_ip(ip, refresh = false)
|
|
927
|
-
JSS::NetworkSegment.network_segments_for_ip ip, refresh, api: self
|
|
928
|
-
end
|
|
929
|
-
|
|
930
|
-
# @deprecated
|
|
931
|
-
#
|
|
932
|
-
# @see {JSS::NetworkSegment.my_network_segments}
|
|
933
|
-
#
|
|
934
|
-
def my_network_segments
|
|
935
|
-
network_segments_for_ip JSS::Client.my_ip_address
|
|
936
|
-
end
|
|
937
|
-
|
|
938
|
-
# Send an MDM command to one or more mobile devices managed by
|
|
939
|
-
# this JSS
|
|
940
|
-
#
|
|
941
|
-
# see {JSS::MobileDevice.send_mdm_command}
|
|
942
|
-
#
|
|
943
|
-
# @deprecated Please use JSS::MobileDevice.send_mdm_command or its
|
|
944
|
-
# convenience methods. @see JSS::MDM
|
|
945
|
-
#
|
|
946
|
-
def send_mobiledevice_mdm_command(targets, command, data = {})
|
|
947
|
-
JSS::MobileDevice.send_mdm_command(targets, command, opts: data, api: self)
|
|
948
|
-
end
|
|
949
|
-
|
|
950
662
|
# Empty all cached lists from this connection
|
|
951
663
|
# then run garbage collection to clear any available memory
|
|
952
664
|
#
|
|
@@ -1052,6 +764,7 @@ module JSS
|
|
|
1052
764
|
#
|
|
1053
765
|
def apply_defaults_from_client(args)
|
|
1054
766
|
return unless JSS::Client.installed?
|
|
767
|
+
|
|
1055
768
|
# these settings can come from the jamf binary config, if this machine is a JSS client.
|
|
1056
769
|
args[:server] ||= JSS::Client.jss_server
|
|
1057
770
|
args[:port] ||= JSS::Client.jss_port.to_i
|
|
@@ -1105,11 +818,13 @@ module JSS
|
|
|
1105
818
|
# keep this basic level of info available for basic authentication
|
|
1106
819
|
# and JSS version checking.
|
|
1107
820
|
begin
|
|
1108
|
-
|
|
1109
|
-
rescue
|
|
821
|
+
data = get_rsrc('jssuser')
|
|
822
|
+
rescue JSS::AuthorizationError
|
|
1110
823
|
raise JSS::AuthenticationError, "Incorrect JSS username or password for '#{@user}@#{@server_host}:#{@port}'."
|
|
1111
824
|
end
|
|
1112
825
|
|
|
826
|
+
@server = JSS::Server.new data[:user], self
|
|
827
|
+
|
|
1113
828
|
min_vers = JSS.parse_jss_version(JSS::MINIMUM_SERVER_VERSION)[:version]
|
|
1114
829
|
return if @server.version >= min_vers # we're good...
|
|
1115
830
|
|
|
@@ -1178,77 +893,69 @@ module JSS
|
|
|
1178
893
|
if SSL_PORTS.include? args[:port]
|
|
1179
894
|
args[:use_ssl] = true unless args[:use_ssl] == false
|
|
1180
895
|
end
|
|
896
|
+
return unless args[:use_ssl]
|
|
897
|
+
|
|
1181
898
|
# if verify_cert is anything but false, we will verify
|
|
1182
|
-
args[:verify_ssl] = args[:verify_cert]
|
|
899
|
+
args[:verify_ssl] = args[:verify_cert] != false
|
|
900
|
+
|
|
901
|
+
# ssl version if not specified
|
|
902
|
+
args[:ssl_version] ||= DFT_SSL_VERSION
|
|
903
|
+
|
|
904
|
+
@ssl_options = {
|
|
905
|
+
verify: args[:verify_ssl],
|
|
906
|
+
version: args[:ssl_version]
|
|
907
|
+
}
|
|
1183
908
|
end
|
|
1184
909
|
|
|
1185
|
-
# Parses the
|
|
1186
|
-
#
|
|
1187
|
-
# and re-raises a JSS::APIError with a more
|
|
1188
|
-
# useful error message.
|
|
1189
|
-
#
|
|
1190
|
-
# @param exception[RestClient::ExceptionWithResponse] the exception to parse
|
|
910
|
+
# Parses the @last_http_response
|
|
911
|
+
# and raises a JSS::APIError with a useful error message.
|
|
1191
912
|
#
|
|
1192
913
|
# @return [void]
|
|
1193
914
|
#
|
|
1194
|
-
def handle_http_error
|
|
1195
|
-
@last_http_response
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
915
|
+
def handle_http_error
|
|
916
|
+
return if @last_http_response.success?
|
|
917
|
+
|
|
918
|
+
case @last_http_response.status
|
|
919
|
+
when 404
|
|
920
|
+
err = JSS::NoSuchItemError
|
|
921
|
+
msg = 'Not Found'
|
|
922
|
+
when 409
|
|
1201
923
|
err = JSS::ConflictError
|
|
1202
|
-
|
|
1203
|
-
|
|
924
|
+
@last_http_response.body =~ /<p>(The server has not .*?)(<|$)/m
|
|
925
|
+
Regexp.last_match(1) || @last_http_response.body =~ %r{<p>Error: (.*?)</p>}
|
|
926
|
+
msg = Regexp.last_match(1)
|
|
927
|
+
when 400
|
|
1204
928
|
err = JSS::BadRequestError
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
929
|
+
@last_http_response.body =~ %r{>Bad Request</p>\n<p>(.*?)</p>\n<p>You can get technical detail}m
|
|
930
|
+
msg = Regexp.last_match(1)
|
|
931
|
+
when 401
|
|
932
|
+
err = JSS::AuthorizationError
|
|
933
|
+
msg = 'You are not authorized to do that.'
|
|
934
|
+
when (500..599)
|
|
935
|
+
err = JSS::APIRequestError
|
|
936
|
+
msg = 'There was an internal server error'
|
|
1208
937
|
else
|
|
1209
938
|
err = JSS::APIRequestError
|
|
1210
|
-
|
|
939
|
+
msg = "There was a error processing your request, status: #{@last_http_response.status}"
|
|
1211
940
|
end
|
|
1212
|
-
exception.http_body =~ msg_matcher
|
|
1213
|
-
msg = Regexp.last_match(1)
|
|
1214
|
-
msg ||= exception.http_body
|
|
1215
941
|
raise err, msg
|
|
1216
942
|
end
|
|
1217
943
|
|
|
1218
|
-
#
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
# @param payload[String] The XML to be passed with the DELETE
|
|
1228
|
-
#
|
|
1229
|
-
# @param additional_headers[Type] See RestClient::Request#execute
|
|
1230
|
-
#
|
|
1231
|
-
# @param &block[Type] See RestClient::Request#execute
|
|
1232
|
-
#
|
|
1233
|
-
# @return [String] the XML response from the server.
|
|
1234
|
-
#
|
|
1235
|
-
def delete_with_payload(rsrc, payload, additional_headers = {}, &block)
|
|
1236
|
-
headers = (@cnx.options[:headers] || {}).merge(additional_headers)
|
|
1237
|
-
@last_http_response = RestClient::Request.execute(
|
|
1238
|
-
@cnx.options.merge(
|
|
1239
|
-
method: :delete,
|
|
1240
|
-
url: @cnx[rsrc].url,
|
|
1241
|
-
payload: payload,
|
|
1242
|
-
headers: headers
|
|
1243
|
-
),
|
|
1244
|
-
&(block || @block)
|
|
1245
|
-
)
|
|
1246
|
-
rescue RestClient::ExceptionWithResponse => e
|
|
1247
|
-
handle_http_error e
|
|
1248
|
-
end # delete_with_payload
|
|
944
|
+
# create the faraday connection object
|
|
945
|
+
def create_connection(pw)
|
|
946
|
+
Faraday.new(@rest_url, ssl: @ssl_options) do |cnx|
|
|
947
|
+
cnx.basic_auth @user, pw
|
|
948
|
+
cnx.options[:timeout] = @timeout
|
|
949
|
+
cnx.options[:open_timeout] = @open_timeout
|
|
950
|
+
cnx.adapter Faraday::Adapter::NetHttp
|
|
951
|
+
end
|
|
952
|
+
end
|
|
1249
953
|
|
|
1250
954
|
end # class APIConnection
|
|
1251
955
|
|
|
956
|
+
# JSS MODULE METHODS
|
|
957
|
+
######################
|
|
958
|
+
|
|
1252
959
|
# Create a new APIConnection object and use it for all
|
|
1253
960
|
# future API calls. If connection options are provided,
|
|
1254
961
|
# they are passed to the connect method immediately, otherwise
|
|
@@ -1276,6 +983,7 @@ module JSS
|
|
|
1276
983
|
#
|
|
1277
984
|
def self.use_api_connection(connection)
|
|
1278
985
|
raise 'API connections must be instances of JSS::APIConnection' unless connection.is_a? JSS::APIConnection
|
|
986
|
+
|
|
1279
987
|
@api = connection
|
|
1280
988
|
end
|
|
1281
989
|
|