ruby-jss 0.8.2 → 0.9.0.b1
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 +37 -1
- data/README.md +15 -15
- data/bin/cgrouper +1 -1
- data/bin/netseg-update +1 -1
- data/lib/jss/api_connection.rb +307 -25
- data/lib/jss/api_object.rb +123 -54
- data/lib/jss/api_object/account.rb +6 -1
- data/lib/jss/api_object/advanced_search.rb +12 -12
- data/lib/jss/api_object/computer.rb +570 -141
- data/lib/jss/api_object/computer_invitation.rb +9 -0
- data/lib/jss/api_object/creatable.rb +1 -1
- data/lib/jss/api_object/distribution_point.rb +1 -1
- data/lib/jss/api_object/extension_attribute.rb +6 -6
- data/lib/jss/api_object/group.rb +1 -1
- data/lib/jss/api_object/matchable.rb +1 -1
- data/lib/jss/api_object/mobile_device.rb +690 -304
- data/lib/jss/api_object/mobile_device_application.rb +11 -1
- data/lib/jss/api_object/package.rb +1 -1
- data/lib/jss/api_object/policy.rb +206 -2
- data/lib/jss/api_object/restricted_software.rb +126 -42
- data/lib/jss/api_object/scopable/scope.rb +20 -21
- data/lib/jss/api_object/self_servable.rb +9 -6
- data/lib/jss/api_object/updatable.rb +1 -1
- data/lib/jss/api_object/uploadable.rb +1 -1
- data/lib/jss/server.rb +57 -56
- data/lib/jss/utility.rb +4 -0
- data/lib/jss/version.rb +1 -1
- metadata +4 -24
data/lib/jss/api_object.rb
CHANGED
@@ -108,24 +108,18 @@ module JSS
|
|
108
108
|
# *NOTE* Some API objects have data broken into subsections, in which case the
|
109
109
|
# VALID_DATA_KEYS are expected in the section :general.
|
110
110
|
#
|
111
|
+
#
|
112
|
+
# === Optional Constants
|
113
|
+
#
|
114
|
+
# ==== OTHER_LOOKUP_KEYS = [Hash{Symbol=>Hash}] Every object can be looked up by
|
115
|
+
# :id and :name, but some have other uniq identifiers that can also be used,
|
116
|
+
# e.g. :serial_number, :mac_address, and so on. This Hash, if defined,
|
117
|
+
# speficies those other keys for the subclass
|
118
|
+
# For more details about this hash, see {APIObject::DEFAULT_LOOKUP_KEYS},
|
119
|
+
# {APIObject.fetch}, and {APIObject#lookup_object_data}
|
120
|
+
#
|
111
121
|
class APIObject
|
112
122
|
|
113
|
-
# Mix-Ins
|
114
|
-
#####################################
|
115
|
-
|
116
|
-
# Class Variables
|
117
|
-
#####################################
|
118
|
-
|
119
|
-
# This Hash holds the most recent API query for a list of all items in any subclass,
|
120
|
-
# keyed by the subclass's RSRC_LIST_KEY. See the self.all class method.
|
121
|
-
#
|
122
|
-
# When the .all method is called without an argument, and this hash has
|
123
|
-
# a matching value, the value is returned, rather than requerying the
|
124
|
-
# API. The first time a class calls .all, or whnever refresh is
|
125
|
-
# not false, the API is queried and the value in this hash is updated.
|
126
|
-
#
|
127
|
-
@@all_items = {}
|
128
|
-
|
129
123
|
# Class Methods
|
130
124
|
#####################################
|
131
125
|
|
@@ -148,7 +142,7 @@ module JSS
|
|
148
142
|
# class methods for accessing those other values as mapped Arrays,
|
149
143
|
# e.g. JSS::Computer.all_udids
|
150
144
|
#
|
151
|
-
# The results of the first query for each subclass is stored in
|
145
|
+
# The results of the first query for each subclass is stored in JSS.api.object_list_cache
|
152
146
|
# and returned at every future call, so as to not requery the server every time.
|
153
147
|
#
|
154
148
|
# To force requerying to get updated data, provided a non-false argument.
|
@@ -161,9 +155,9 @@ module JSS
|
|
161
155
|
#
|
162
156
|
def self.all(refresh = false)
|
163
157
|
raise JSS::UnsupportedError, '.all can only be called on subclasses of JSS::APIObject' if self == JSS::APIObject
|
164
|
-
|
165
|
-
return
|
166
|
-
|
158
|
+
JSS.api.object_list_cache[self::RSRC_LIST_KEY] = nil if refresh
|
159
|
+
return JSS.api.object_list_cache[self::RSRC_LIST_KEY] if JSS.api.object_list_cache[self::RSRC_LIST_KEY]
|
160
|
+
JSS.api.object_list_cache[self::RSRC_LIST_KEY] = JSS.api.get_rsrc(self::RSRC_BASE)[self::RSRC_LIST_KEY]
|
167
161
|
end
|
168
162
|
|
169
163
|
# Returns an Array of the JSS id numbers of all the members
|
@@ -174,7 +168,7 @@ module JSS
|
|
174
168
|
#
|
175
169
|
# @param refresh[Boolean] should the data be re-queried from the API?
|
176
170
|
#
|
177
|
-
# @return [Array<Integer>] the ids of all
|
171
|
+
# @return [Array<Integer>] the ids of all it1ems of this subclass in the JSS
|
178
172
|
#
|
179
173
|
def self.all_ids(refresh = false)
|
180
174
|
all(refresh).map { |i| i[:id] }
|
@@ -238,9 +232,9 @@ module JSS
|
|
238
232
|
# @return [Hash{Integer => Object}] the objects requested
|
239
233
|
def self.all_objects(refresh = false)
|
240
234
|
objects_key = "#{self::RSRC_LIST_KEY}_objects".to_sym
|
241
|
-
|
242
|
-
return
|
243
|
-
|
235
|
+
JSS.api.object_list_cache[objects_key] = nil if refresh
|
236
|
+
return JSS.api.object_list_cache[objects_key] if JSS.api.object_list_cache[objects_key]
|
237
|
+
JSS.api.object_list_cache[objects_key] = all(refresh).map { |o| new id: o[:id] }
|
244
238
|
end
|
245
239
|
|
246
240
|
# Return true or false if an object of this subclass
|
@@ -368,6 +362,44 @@ module JSS
|
|
368
362
|
end
|
369
363
|
end
|
370
364
|
|
365
|
+
# What are all the lookup keys available for this class?
|
366
|
+
#
|
367
|
+
# @return [Array<Symbol>] the DEFAULT_LOOKUP_KEYS plus any OTHER_LOOKUP_KEYS
|
368
|
+
# defined for this class
|
369
|
+
#
|
370
|
+
def self.lookup_keys
|
371
|
+
return DEFAULT_LOOKUP_KEYS.keys unless defined? self::OTHER_LOOKUP_KEYS
|
372
|
+
DEFAULT_LOOKUP_KEYS.keys + self::OTHER_LOOKUP_KEYS.keys
|
373
|
+
end
|
374
|
+
|
375
|
+
# @return [Hash] the available lookup keys mapped to the appropriate
|
376
|
+
# resource key for building a REST url to retrieve an object.
|
377
|
+
#
|
378
|
+
def self.rsrc_keys
|
379
|
+
hash = {}
|
380
|
+
all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
|
381
|
+
DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
|
382
|
+
else
|
383
|
+
DEFAULT_LOOKUP_KEYS
|
384
|
+
end
|
385
|
+
all_keys.each { |key, deets| hash[key] = deets[:rsrc_key]}
|
386
|
+
hash
|
387
|
+
end
|
388
|
+
|
389
|
+
# @return [Hash] the available lookup keys mapped to the appropriate
|
390
|
+
# list class method (e.g. id: :all_ids )
|
391
|
+
#
|
392
|
+
def self.lookup_key_list_methods
|
393
|
+
hash = {}
|
394
|
+
all_keys = if defined?(self::OTHER_LOOKUP_KEYS)
|
395
|
+
DEFAULT_LOOKUP_KEYS.merge self::OTHER_LOOKUP_KEYS
|
396
|
+
else
|
397
|
+
DEFAULT_LOOKUP_KEYS
|
398
|
+
end
|
399
|
+
all_keys.each { |key, deets| hash[key] = deets[:list]}
|
400
|
+
hash
|
401
|
+
end
|
402
|
+
|
371
403
|
# Retrieve an object from the API.
|
372
404
|
#
|
373
405
|
# This is the preferred way to retrieve existing objects from the JSS.
|
@@ -382,11 +414,26 @@ module JSS
|
|
382
414
|
#
|
383
415
|
# @return [APIObject] The ruby-instance of a JSS object
|
384
416
|
#
|
385
|
-
def self.fetch(
|
417
|
+
def self.fetch(arg)
|
386
418
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
387
|
-
|
388
|
-
|
389
|
-
|
419
|
+
|
420
|
+
# if given a hash (or a colletion of named params)
|
421
|
+
# pass to .new
|
422
|
+
if arg.is_a? Hash
|
423
|
+
raise ArgumentError, 'Use .create to create new JSS objects' if arg[:id] == :new
|
424
|
+
return new arg
|
425
|
+
end
|
426
|
+
|
427
|
+
# loop thru the lookup_key list methods for this class
|
428
|
+
# and if it's result includes the desired value,
|
429
|
+
# the pass they key and arg to .new
|
430
|
+
lookup_key_list_methods.each do |key, method_name|
|
431
|
+
return new({key => arg}) if self.send(method_name).include? arg
|
432
|
+
end # each key
|
433
|
+
|
434
|
+
# if we're here, we couldn't find a matching object
|
435
|
+
raise NoSuchItemError, "No #{self::RSRC_OBJECT_KEY} found matching '#{arg}'"
|
436
|
+
end # fetch
|
390
437
|
|
391
438
|
# Make a ruby instance of a not-yet-existing APIObject.
|
392
439
|
#
|
@@ -404,14 +451,13 @@ module JSS
|
|
404
451
|
#
|
405
452
|
# @return [APIObject] The un-created ruby-instance of a JSS object
|
406
453
|
#
|
407
|
-
def self.make(
|
454
|
+
def self.make(args = {})
|
408
455
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
409
456
|
raise ArgumentError, "Use '#{self.class}.fetch id: xx' to retrieve existing JSS objects" if args[:id]
|
410
457
|
args[:id] = :new
|
411
458
|
new args
|
412
459
|
end
|
413
460
|
|
414
|
-
|
415
461
|
### Class Constants
|
416
462
|
#####################################
|
417
463
|
|
@@ -420,13 +466,37 @@ module JSS
|
|
420
466
|
#
|
421
467
|
REQUIRED_DATA_KEYS = [:id, :name].freeze
|
422
468
|
|
423
|
-
# By
|
424
|
-
#
|
425
|
-
# as the second argument to super(initialize)
|
426
|
-
# The keys must be Symbols that match the keyname in the resource url.
|
427
|
-
# e.g. :serialnumber for JSSResource/computers/serialnumber/xxxxx
|
469
|
+
# All API objects have an id and a name. As such By these keys are available
|
470
|
+
# for object lookups.
|
428
471
|
#
|
429
|
-
|
472
|
+
# Others can be defined by subclasses in their OTHER_LOOKUP_KEYS constant
|
473
|
+
# which has the same format, described here:
|
474
|
+
#
|
475
|
+
# The merged Hashes DEFAULT_LOOKUP_KEYS and OTHER_LOOKUP_KEYS
|
476
|
+
# define what unique identifiers can be passed as parameters to the
|
477
|
+
# fetch method for retrieving an object from the API.
|
478
|
+
# They also define the class methods that return a list (Array) of all such
|
479
|
+
# identifiers for the class (e.g. the :all_ids class method returns an array
|
480
|
+
# of all id's for an APIObject subclass)
|
481
|
+
#
|
482
|
+
# Since there's often a discrepency between the name of the identifier as
|
483
|
+
# an attribute (e.g. serial_number) and the REST resource key for
|
484
|
+
# retrieving that object (e.g. ../computers/serialnumber/xxxxx) this hash
|
485
|
+
# also explicitly provides the REST resource key for a given lookup key, so
|
486
|
+
# e.g. both serialnumber and serial_number can be used, and both will have
|
487
|
+
# the resource key 'serialnumber' and the list method ':all_serial_numbers'
|
488
|
+
#
|
489
|
+
# Here's how the Hash is structured, using serialnumber as an example:
|
490
|
+
#
|
491
|
+
# LOOKUP_KEYS = {
|
492
|
+
# serialnumber: {rsrc_key: :serialnumber, list: :all_serial_numbers},
|
493
|
+
# serial_number: {rsrc_key: :serialnumber, list: :all_serial_numbers}
|
494
|
+
# }
|
495
|
+
#
|
496
|
+
DEFAULT_LOOKUP_KEYS = {
|
497
|
+
id: {rsrc_key: :id, list: :all_ids},
|
498
|
+
name: {rsrc_key: :name, list: :all_names}
|
499
|
+
}.freeze
|
430
500
|
|
431
501
|
# Attributes
|
432
502
|
#####################################
|
@@ -461,20 +531,13 @@ module JSS
|
|
461
531
|
# @option args :name[String] the name to look up
|
462
532
|
#
|
463
533
|
# @option args :data[Hash] the JSON output of a separate {JSS::APIConnection} query
|
534
|
+
# NOTE: This arg is deprecated and will be removed in a future release.
|
464
535
|
#
|
465
|
-
# @param other_lookup_keys[Array<Symbol>] Hash keys other than :id and :name, by which an API
|
466
|
-
# lookup may be performed.
|
467
536
|
#
|
468
|
-
def initialize(args = {}
|
469
|
-
args[:other_lookup_keys] ||= other_lookup_keys
|
537
|
+
def initialize(args = {})
|
470
538
|
|
471
539
|
raise JSS::UnsupportedError, 'JSS::APIObject cannot be instantiated' if self.class == JSS::APIObject
|
472
540
|
|
473
|
-
### what lookup key are we using, if any?
|
474
|
-
lookup_keys = DEFAULT_LOOKUP_KEYS
|
475
|
-
lookup_keys += self.class::OTHER_LOOKUP_KEYS if defined? self.class::OTHER_LOOKUP_KEYS
|
476
|
-
lookup_key = (lookup_keys & args.keys)[0]
|
477
|
-
|
478
541
|
####### Previously looked-up JSON data
|
479
542
|
# DEPRECATED: pre-lookedup data is never used
|
480
543
|
# and support for it will be going away.
|
@@ -486,9 +549,9 @@ module JSS
|
|
486
549
|
|
487
550
|
###### Make a new one in the JSS, but only if we've included the Creatable module
|
488
551
|
elsif args[:id] == :new
|
489
|
-
|
490
552
|
validate_init_for_creation(args)
|
491
553
|
setup_object_for_creation(args)
|
554
|
+
|
492
555
|
return
|
493
556
|
|
494
557
|
###### Look up the data via the API
|
@@ -496,6 +559,7 @@ module JSS
|
|
496
559
|
@init_data = look_up_object_data(args)
|
497
560
|
end ## end arg parsing
|
498
561
|
|
562
|
+
|
499
563
|
parse_init_data
|
500
564
|
@need_to_update = false
|
501
565
|
end # init
|
@@ -596,7 +660,7 @@ module JSS
|
|
596
660
|
#
|
597
661
|
def delete
|
598
662
|
return nil unless @in_jss
|
599
|
-
JSS
|
663
|
+
JSS.api.delete_rsrc @rest_rsrc
|
600
664
|
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
601
665
|
@id = nil
|
602
666
|
@in_jss = false
|
@@ -668,16 +732,20 @@ module JSS
|
|
668
732
|
#
|
669
733
|
def look_up_object_data(args)
|
670
734
|
# what lookup key are we using?
|
671
|
-
|
672
|
-
lookup_key = (
|
735
|
+
lookup_keys = self.class.lookup_keys
|
736
|
+
lookup_key = (self.class.lookup_keys & args.keys)[0]
|
737
|
+
raise JSS::MissingDataError, "Args must include a lookup key, one of: :#{lookup_keys.join(', :')}" unless lookup_key
|
738
|
+
rsrc_key = self.class.rsrc_keys[lookup_key]
|
673
739
|
|
674
|
-
|
740
|
+
rsrc = "#{self.class::RSRC_BASE}/#{rsrc_key}/#{args[lookup_key]}"
|
675
741
|
|
676
|
-
|
742
|
+
# if needed, a non-standard object key can be passed by a subclass.
|
743
|
+
# e.g. User when loookup is by email.
|
744
|
+
rsrc_object_key = args[:rsrc_object_key] ? args[:rsrc_object_key] : self.class::RSRC_OBJECT_KEY
|
677
745
|
|
678
|
-
return JSS
|
746
|
+
return JSS.api.get_rsrc(rsrc)[rsrc_object_key]
|
679
747
|
rescue RestClient::ResourceNotFound
|
680
|
-
raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{
|
748
|
+
raise NoSuchItemError, "No #{self.class::RSRC_OBJECT_KEY} found matching: #{rsrc_key}/#{args[lookup_key]}"
|
681
749
|
end
|
682
750
|
|
683
751
|
# Start examining the @init_data recieved from the API
|
@@ -685,6 +753,7 @@ module JSS
|
|
685
753
|
# @return [void]
|
686
754
|
#
|
687
755
|
def parse_init_data
|
756
|
+
@init_data ||= {}
|
688
757
|
# set empty strings to nil
|
689
758
|
@init_data.jss_nillify! '', :recurse
|
690
759
|
|
@@ -802,7 +871,7 @@ module JSS
|
|
802
871
|
def setup_object_for_creation(args)
|
803
872
|
# NOTE: subclasses may want to pre-populate more keys in @init_data when :id == :new
|
804
873
|
# then parse them into attributes later.
|
805
|
-
@init_data =
|
874
|
+
@init_data = args
|
806
875
|
@name = args[:name]
|
807
876
|
@in_jss = false
|
808
877
|
@rest_rsrc = "#{self.class::RSRC_BASE}/name/#{CGI.escape @name}"
|
@@ -61,7 +61,12 @@ module JSS
|
|
61
61
|
RSRC_OBJECT_KEY = :account
|
62
62
|
|
63
63
|
# these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
|
64
|
-
OTHER_LOOKUP_KEYS =
|
64
|
+
OTHER_LOOKUP_KEYS = {
|
65
|
+
userid: {rsrc_key: :userid, list: :all_user_ids},
|
66
|
+
username: {rsrc_key: :username, list: :all_user_names},
|
67
|
+
groupid: {rsrc_key: :groupid, list: :all_group_ids},
|
68
|
+
groupname: {rsrc_key: :groupname, list: :all_group_names}
|
69
|
+
}.freeze
|
65
70
|
|
66
71
|
# Class Methods
|
67
72
|
#####################################
|
@@ -167,11 +167,11 @@ module JSS
|
|
167
167
|
raise JSS::InvalidDataError, 'JSS::Criteriable::Criteria instance required' unless @criteria.is_a? JSS::Criteriable::Criteria
|
168
168
|
raise JSS::InvalidDataError, 'display_fields must be an Array.' unless @display_fields.is_a? Array
|
169
169
|
|
170
|
-
orig_timeout = JSS
|
171
|
-
JSS
|
170
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
171
|
+
JSS.api_connection.timeout = 1800
|
172
172
|
super()
|
173
173
|
requery_search_results if get_results
|
174
|
-
JSS
|
174
|
+
JSS.api_connection.timeout = orig_timeout
|
175
175
|
|
176
176
|
@id # remember to return the id
|
177
177
|
end
|
@@ -185,11 +185,11 @@ module JSS
|
|
185
185
|
# @return [Integer] the id of the updated search
|
186
186
|
#
|
187
187
|
def update(get_results = false)
|
188
|
-
orig_timeout = JSS
|
189
|
-
JSS
|
188
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
189
|
+
JSS.api_connection.timeout = 1800
|
190
190
|
super()
|
191
191
|
requery_search_results if get_results
|
192
|
-
JSS
|
192
|
+
JSS.api_connection.timeout = orig_timeout
|
193
193
|
|
194
194
|
@id # remember to return the id
|
195
195
|
end
|
@@ -201,17 +201,17 @@ module JSS
|
|
201
201
|
# @return [Array<Hash>] the new search results
|
202
202
|
#
|
203
203
|
def requery_search_results
|
204
|
-
orig_open_timeout = JSS
|
205
|
-
orig_timeout = JSS
|
206
|
-
JSS
|
207
|
-
JSS
|
204
|
+
orig_open_timeout = JSS.api_connection.cnx.options[:open_timeout]
|
205
|
+
orig_timeout = JSS.api_connection.cnx.options[:timeout]
|
206
|
+
JSS.api_connection.timeout = 1800
|
207
|
+
JSS.api_connection.open_timeout = 1800
|
208
208
|
begin
|
209
209
|
requery = self.class.new(id: @id)
|
210
210
|
@search_results = requery.search_results
|
211
211
|
@result_display_keys = requery.result_display_keys
|
212
212
|
ensure
|
213
|
-
JSS
|
214
|
-
JSS
|
213
|
+
JSS.api_connection.timeout = orig_timeout
|
214
|
+
JSS.api_connection.open_timeout = orig_open_timeout
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
@@ -26,24 +26,14 @@
|
|
26
26
|
###
|
27
27
|
module JSS
|
28
28
|
|
29
|
-
# Module Constants
|
30
|
-
#####################################
|
31
|
-
|
32
|
-
# Module Variables
|
33
|
-
#####################################
|
34
|
-
|
35
|
-
# Module Methods
|
36
|
-
#####################################
|
37
|
-
|
38
29
|
# This class represents a Computer in the JSS.
|
39
30
|
#
|
40
|
-
# ===Adding Computers to the JSS
|
31
|
+
# === Adding Computers to the JSS
|
41
32
|
#
|
42
|
-
#
|
43
|
-
#
|
33
|
+
# At the moment, this class cannot be used to add new Computers to the JSS.
|
34
|
+
# Please use other methods (like the Recon App or QuickAdd package)
|
44
35
|
#
|
45
|
-
#
|
46
|
-
# ===Editing values
|
36
|
+
# === Editing values
|
47
37
|
#
|
48
38
|
# Any data that arrives in the JSS via an "inventory update"
|
49
39
|
# (a.k.a. 'recon') cannot be modified through this class, or the API.
|
@@ -66,27 +56,91 @@ module JSS
|
|
66
56
|
# After making any changes, you must call #update to send those
|
67
57
|
# changes to the server.
|
68
58
|
#
|
69
|
-
# ---
|
70
59
|
# === MDM Commands
|
71
60
|
#
|
72
|
-
#
|
73
|
-
#
|
61
|
+
# The following methods can be used to send an APNS command to the
|
62
|
+
# computer represented by an instance of JSS::Computer, equivalent to
|
63
|
+
# clicking one of the buttons on the Management Commands section of the
|
64
|
+
# Management tab of the Computer details page in the JSS UI.
|
65
|
+
#
|
66
|
+
# - {#blank_push} (aliases blank, noop, send_blank_push)
|
67
|
+
# - {#device_lock} (aliases lock, lock_device)
|
68
|
+
# - {#erase_device} (aliases wipe)
|
69
|
+
# - {#remove_mdm_profile}
|
70
|
+
#
|
71
|
+
# To send an MDM command without making a Computer instance, use the class
|
72
|
+
# {JSS::Computer.send_mdm_command} which can take multiple computer
|
73
|
+
# identifiers at once.
|
74
|
+
#
|
75
|
+
# NOTE: the poorly named 'UnmanageDevice' command via the API is implemented
|
76
|
+
# as the {#remove_mdm_profile} method (which is its name in the webUI).
|
77
|
+
# Calling that method will NOT unmanage the machine from the JSS's point
|
78
|
+
# of view, it will just remove the mdm management profile from the machine
|
79
|
+
# and all configuration profiles that were installed via the JSS. Those
|
80
|
+
# profiles may be re-installed automatically later if the computer is still in
|
81
|
+
# scope for them
|
82
|
+
#
|
83
|
+
# The {#make_unmanaged} method also removes the mdm profile, but actually
|
84
|
+
# does make the machine unmanged by the JSS, setting the management acct to
|
85
|
+
# nil, and requring re-enrollment.
|
86
|
+
#
|
87
|
+
# === Computer History
|
88
|
+
#
|
89
|
+
# Computer instances can now retrieve their management history from the JSS.
|
90
|
+
#
|
91
|
+
# The full history data is available from the {#history} method, but beware that
|
92
|
+
# it is very large.
|
93
|
+
#
|
94
|
+
# Subsets of that history have their own methods, which are faster and only retrieve
|
95
|
+
# the subset requested. See {#usage_logs}, {#audits}, {#policy_los},
|
96
|
+
# {#completed_policies}, {#failed_polices}, {#casper_remote_logs},
|
97
|
+
# {#screen_sharing_logs}, {#casper_imaging_logs}, {#commands},
|
98
|
+
# {#user_location_history},and {#app_store_app_history}
|
99
|
+
#
|
100
|
+
# When any of the history methods is used the first time, the data is read
|
101
|
+
# from the API and cached internally, and that data is
|
102
|
+
# used for all future calls.. To re-read the data from the API and re-cache it,
|
103
|
+
# provide any non-false parameter to the subset methods , or `refresh: true`
|
104
|
+
# to the main {#history} method.
|
105
|
+
#
|
106
|
+
# === Appication Usage History
|
107
|
+
#
|
108
|
+
# Computer Instances now have access to their Application Usage history
|
109
|
+
# via the {#application_usage} method.
|
110
|
+
# Call the method with a start-date value (either a String or a Time object)
|
111
|
+
# and an optional end-date value. If you omite the end-date, the start-date
|
112
|
+
# is used and you'll see usage for just that day.
|
113
|
+
#
|
114
|
+
# See {#application_usage} for details about the data returned.
|
74
115
|
#
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
116
|
+
# NOTE: your JSS must be gathering Appication Usage data in order
|
117
|
+
# for any data to be returned, and the usage history will only go back as
|
118
|
+
# far as your setting for flushing of Application Usage Logs.
|
78
119
|
#
|
79
|
-
#
|
80
|
-
# - #blank_push (aliases blank, noop, send_blank_push)
|
81
|
-
# - #device_lock (aliases lock, lock_device)
|
82
|
-
# - #erase_device (aliases wipe)
|
120
|
+
# === Management Data
|
83
121
|
#
|
84
|
-
#
|
122
|
+
# The computers 'manamgement data', as presented on the 'Management' tab of
|
123
|
+
# the computer's detail page in the JSS web UI, is available from the
|
124
|
+
# {#management_data} method. That method may return a large dataset,
|
125
|
+
# unless a subset is requested.
|
85
126
|
#
|
86
|
-
#
|
127
|
+
# Subsets of management data have their own methods, which are faster and
|
128
|
+
# only retrieve the subset requested. See {#smart_groups}, {#static_groups},
|
129
|
+
# {#policies}, {#configuration_profiles}, {#ebooks}, {#app_store_apps},
|
130
|
+
# {#restricted_software}, and {#patch_titles}
|
87
131
|
#
|
88
|
-
#
|
89
|
-
#
|
132
|
+
# The subset methods can take an 'only:' parameter, which is a symbol specifying
|
133
|
+
# the value you care to see. For example {#smart_groups} returns an array
|
134
|
+
# of hashes, one for each smart_group the computer is in. Those hashes
|
135
|
+
# have two keys, :name, and :id. However if you only want an array of
|
136
|
+
# names, you can call `smart_groups only: :name`
|
137
|
+
#
|
138
|
+
# When any of the manamgement data methods are used the first time, the data
|
139
|
+
# is read from the API and cached internally, the cache is then
|
140
|
+
# used for all future calls. To re-read the data from the API and re-cache it,
|
141
|
+
# provide `refresh: true` to any of the manamgement data methods.
|
142
|
+
#
|
143
|
+
# === Other Methods
|
90
144
|
#
|
91
145
|
# - {#set_management_to} change the management acct and passwd for this computer, aliased to #make_managed
|
92
146
|
# - requires calling #update to push changes to the server
|
@@ -118,14 +172,185 @@ module JSS
|
|
118
172
|
|
119
173
|
extend JSS::Matchable
|
120
174
|
|
121
|
-
# Class
|
175
|
+
# Class Constants
|
122
176
|
#####################################
|
123
177
|
|
124
|
-
|
178
|
+
# The base for REST resources of this class
|
179
|
+
RSRC_BASE = 'computers'.freeze
|
180
|
+
|
181
|
+
# The (temporary?) list-resource
|
182
|
+
LIST_RSRC = "#{RSRC_BASE}/subset/basic".freeze
|
183
|
+
|
184
|
+
# the hash key used for the JSON list output of all objects in the JSS
|
185
|
+
RSRC_LIST_KEY = :computers
|
186
|
+
|
187
|
+
# The hash key used for the JSON object output.
|
188
|
+
# It's also used in various error messages
|
189
|
+
RSRC_OBJECT_KEY = :computer
|
190
|
+
|
191
|
+
# these keys, as well as :id and :name, are present in valid API JSON data for this class
|
192
|
+
# DEPRECATED, with be removed in a future release.
|
193
|
+
VALID_DATA_KEYS = %i[sus distribution_point alt_mac_address].freeze
|
194
|
+
|
195
|
+
# these keys, as well as :id and :name, can be used to look up objects of this class in the JSS
|
196
|
+
OTHER_LOOKUP_KEYS = {
|
197
|
+
udid: { rsrc_key: :udid, list: :all_udids },
|
198
|
+
serialnumber: { rsrc_key: :serialnumber, list: :all_serial_numbers },
|
199
|
+
serial_number: { rsrc_key: :serialnumber, list: :all_serial_numbers },
|
200
|
+
macaddress: { rsrc_key: :macaddress, list: :all_mac_addresses },
|
201
|
+
mac_address: { rsrc_key: :macaddress, list: :all_mac_addresses }
|
202
|
+
}.freeze
|
203
|
+
|
204
|
+
# This class lets us seach for computers
|
205
|
+
SEARCH_CLASS = JSS::AdvancedComputerSearch
|
206
|
+
|
207
|
+
# This is the class for relevant Extension Attributes
|
208
|
+
EXT_ATTRIB_CLASS = JSS::ComputerExtensionAttribute
|
209
|
+
|
210
|
+
# Boot partitions are noted with the string "(Boot Partition)" at the end
|
211
|
+
BOOT_FLAG = ' (Boot Partition)'.freeze
|
212
|
+
|
213
|
+
# file uploads can send attachments to the JSS using :computers as the sub-resource.
|
214
|
+
UPLOAD_TYPES = { attachment: :computers }.freeze
|
215
|
+
|
216
|
+
# The base REST resource for sending computer MDM commands
|
217
|
+
COMPUTER_MDM_RSRC = 'computercommands/command'.freeze
|
218
|
+
|
219
|
+
# A mapping of Symbols available to the send_mdm_command class method, to
|
220
|
+
# the String commands actuallly sent via the API.
|
221
|
+
COMPUTER_MDM_COMMANDS = {
|
222
|
+
blank_push: 'BlankPush',
|
223
|
+
blankpush: 'BlankPush',
|
224
|
+
send_blank_push: 'BlankPush',
|
225
|
+
blank: 'BlankPush',
|
226
|
+
noop: 'BlankPush',
|
227
|
+
device_lock: 'DeviceLock',
|
228
|
+
devicelock: 'DeviceLock',
|
229
|
+
lock: 'DeviceLock',
|
230
|
+
lock_device: 'DeviceLock',
|
231
|
+
erase_device: 'EraseDevice',
|
232
|
+
erasedevice: 'EraseDevice',
|
233
|
+
erase: 'EraseDevice',
|
234
|
+
wipe: 'EraseDevice',
|
235
|
+
unmanage_device: 'UnmanageDevice',
|
236
|
+
unmanagedevice: 'UnmanageDevice',
|
237
|
+
unmanage: 'UnmanageDevice'
|
238
|
+
}.freeze
|
239
|
+
|
240
|
+
# these MDM commands require a passcode
|
241
|
+
COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE = %w[DeviceLock EraseDevice].freeze
|
242
|
+
|
243
|
+
# The API resource for app usage
|
244
|
+
APPLICATION_USAGE_RSRC = 'computerapplicationusage'.freeze
|
245
|
+
|
246
|
+
# The date format for retrieving usage data
|
247
|
+
APPLICATION_USAGE_DATE_FMT = '%Y-%m-%d'.freeze
|
248
|
+
|
249
|
+
# The classes that can be used with the date format
|
250
|
+
APPLICATION_USAGE_DATE_CLASSES = [Time, DateTime, Date].freeze
|
251
|
+
|
252
|
+
# The top-level hash key of the raw app usage data
|
253
|
+
APPLICATION_USAGE_KEY = :computer_application_usage
|
254
|
+
|
255
|
+
# The API resource for computer_management data
|
256
|
+
MGMT_DATA_RSRC = 'computermanagement'.freeze
|
257
|
+
|
258
|
+
# The top-level hash key of the computer_management data
|
259
|
+
MGMT_DATA_KEY = :computer_management
|
260
|
+
|
261
|
+
# Thes are both the subset names in the resrouce URLS (when
|
262
|
+
# converted to strings) and the second-level hash key of the
|
263
|
+
# returned subset data.
|
264
|
+
MGMT_DATA_SUBSETS = %i[
|
265
|
+
smart_groups
|
266
|
+
static_groups
|
267
|
+
mac_app_store_apps
|
268
|
+
policies
|
269
|
+
ebooks
|
270
|
+
os_x_configuration_profiles
|
271
|
+
restricted_software
|
272
|
+
patch_reporting_software_titles
|
273
|
+
].freeze
|
274
|
+
|
275
|
+
# The API Resource for the computer checkin settings
|
276
|
+
CHECKIN_RSRC = 'computercheckin'.freeze
|
277
|
+
|
278
|
+
# The top-level hash key for the checkin settings
|
279
|
+
CHECKIN_KEY = :computer_check_in
|
280
|
+
|
281
|
+
# The API Resource for the computer inventory collection settings
|
282
|
+
INV_COLLECTION_RSRC = 'computerinventorycollection'.freeze
|
283
|
+
|
284
|
+
# The top-level hash key for the inventory collection settings
|
285
|
+
INV_COLLECTION_KEY = :computer_inventory_collection
|
286
|
+
|
287
|
+
# The API Resource for the computer history data
|
288
|
+
HISTORY_RSRC = 'computerhistory'.freeze
|
289
|
+
|
290
|
+
# The top-level hash key for the history data
|
291
|
+
HISTORY_KEY = :computer_history
|
292
|
+
|
293
|
+
# The keys are both the subset names in the resrouce URLS (when
|
294
|
+
# converted to strings) and the second-level hash key of the
|
295
|
+
# returned subset data.
|
296
|
+
#
|
297
|
+
# The values are the key within each history item that contains the
|
298
|
+
# 'epoch' timestamp, for conver
|
299
|
+
HISTORY_SUBSETS = %i[
|
300
|
+
computer_usage_logs
|
301
|
+
audits
|
302
|
+
policy_logs
|
303
|
+
casper_remote_logs
|
304
|
+
screen_sharing_logs
|
305
|
+
casper_imaging_logs
|
306
|
+
commands
|
307
|
+
user_location
|
308
|
+
mac_app_store_applications
|
309
|
+
].freeze
|
310
|
+
|
311
|
+
# HISTORY_SUBSETS = %i(
|
312
|
+
# computer_usage_logs date_time_epoch
|
313
|
+
# audits
|
314
|
+
# policy_logs date_completed_epoch
|
315
|
+
# casper_remote_logs date_time_epoch
|
316
|
+
# screen_sharing_logs date_time_epoch
|
317
|
+
# casper_imaging_logs
|
318
|
+
# commands completed_epoch
|
319
|
+
# user_location
|
320
|
+
# mac_app_store_applications
|
321
|
+
# ).freeze
|
322
|
+
|
323
|
+
POLICY_STATUS_COMPLETED = 'Completed'.freeze
|
324
|
+
|
325
|
+
POLICY_STATUS_FAILED = 'Failed'.freeze
|
326
|
+
|
327
|
+
POLICY_STATUS_PENDING = 'Pending'.freeze
|
125
328
|
|
126
329
|
# Class Methods
|
127
330
|
#####################################
|
128
331
|
|
332
|
+
# Display the current Computer CheckIn settings in the JSS.
|
333
|
+
# Currently this is read-only in ruby-jss, even tho the API
|
334
|
+
# allows updating.
|
335
|
+
#
|
336
|
+
# @return [Hash] the Computer Checkin Settings from the
|
337
|
+
# currently connected JSS.
|
338
|
+
#
|
339
|
+
def self.checkin_settings
|
340
|
+
JSS.api_connection.get_rsrc(CHECKIN_RSRC)[CHECKIN_KEY]
|
341
|
+
end
|
342
|
+
|
343
|
+
# Display the current Computer Inventory Collection settings in the JSS.
|
344
|
+
# Currently this is read-only in ruby-jss, even tho the API
|
345
|
+
# allows updating.
|
346
|
+
#
|
347
|
+
# @return [Hash] the Computer Inventpry Collection Settings from the
|
348
|
+
# currently connected JSS.
|
349
|
+
#
|
350
|
+
def self.inventory_collection_settings
|
351
|
+
JSS.api_connection.get_rsrc(INV_COLLECTION_RSRC)[INV_COLLECTION_KEY]
|
352
|
+
end
|
353
|
+
|
129
354
|
# A larger set of info about the computers in the JSS.
|
130
355
|
#
|
131
356
|
# Casper 9.4 introduced the API Resource /computers/subset/basic
|
@@ -143,9 +368,9 @@ module JSS
|
|
143
368
|
# @return [Array<Hash{:name=>String, :id=> Integer}>]
|
144
369
|
#
|
145
370
|
def self.all(refresh = false)
|
146
|
-
|
147
|
-
return
|
148
|
-
|
371
|
+
JSS.api.object_list_cache[RSRC_LIST_KEY] = nil if refresh
|
372
|
+
return JSS.api.object_list_cache[RSRC_LIST_KEY] if JSS.api.object_list_cache[RSRC_LIST_KEY]
|
373
|
+
JSS.api.object_list_cache[RSRC_LIST_KEY] = JSS.api_connection.get_rsrc(self::LIST_RSRC)[self::RSRC_LIST_KEY]
|
149
374
|
end
|
150
375
|
|
151
376
|
# @return [Array<String>] all computer serial numbers in the jss
|
@@ -170,7 +395,7 @@ module JSS
|
|
170
395
|
|
171
396
|
# @return [Array<Hash>] all unmanaged computers in the jss
|
172
397
|
def self.all_unmanaged(refresh = false)
|
173
|
-
all(refresh).
|
398
|
+
all(refresh).reject { |d| d[:managed] }
|
174
399
|
end
|
175
400
|
|
176
401
|
# @return [Array<Hash>] all laptop computers in the jss
|
@@ -200,7 +425,7 @@ module JSS
|
|
200
425
|
|
201
426
|
# @return [Array<Hash>] all desktop macs in the jss
|
202
427
|
def self.all_desktops(refresh = false)
|
203
|
-
all(refresh).
|
428
|
+
all(refresh).reject { |d| d[:model] =~ /serve|book/i }
|
204
429
|
end
|
205
430
|
|
206
431
|
# @return [Array<Hash>] all imacs in the jss
|
@@ -218,88 +443,53 @@ module JSS
|
|
218
443
|
all(refresh).select { |d| d[:model] =~ /^macpro/i }
|
219
444
|
end
|
220
445
|
|
221
|
-
# Send an MDM command to
|
222
|
-
#
|
223
|
-
# @param computer[String,Integer] the name or id of the computer to recieve the command
|
224
|
-
# @param command[Symbol] the command to send, one of the keys of COMPUTER_MDM_COMMANDS
|
225
|
-
#
|
226
|
-
# @return [true] if the command was sent
|
227
|
-
#
|
228
|
-
|
229
|
-
# Not functional until I get more docs from JAMF
|
446
|
+
# Send an MDM command to one or more managed computers by id or name
|
230
447
|
#
|
231
|
-
# def self.send_mdm_command(computer,command)
|
232
448
|
#
|
233
|
-
#
|
449
|
+
# @param targets[String,Integer,Array<String,Integer>]
|
450
|
+
# the name or id of the computer to receive the command, or
|
451
|
+
# an array of such names or ids, or a comma-separated string
|
452
|
+
# of them.
|
453
|
+
# @param command[Symbol] the command to send, one of the keys
|
454
|
+
# of COMPUTER_MDM_COMMANDS
|
234
455
|
#
|
235
|
-
#
|
236
|
-
# the_id = nil
|
456
|
+
# @param passcode[String] some commands require a 6-character passcode
|
237
457
|
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
# else
|
241
|
-
# the_id = self.map_all_ids_to(:name).invert[computer]
|
242
|
-
# end
|
458
|
+
# @return [String] The uuid of the MDM command sent, if applicable
|
459
|
+
# (blank pushes do not generate uuids)
|
243
460
|
#
|
244
|
-
|
245
|
-
|
246
|
-
# response =~ %r{<notification_sent>(.+)</notification_sent>}
|
247
|
-
# return ($1 and $1 == "true")
|
248
|
-
# end
|
249
|
-
# raise JSS::UnmanagedError, "Cannot send command to unknown/unmanaged computer '#{computer}'"
|
250
|
-
# end
|
461
|
+
def self.send_mdm_command(targets, command, passcode = nil)
|
462
|
+
raise JSS::NoSuchItemError, "Unknown command '#{command}'" unless COMPUTER_MDM_COMMANDS.keys.include? command
|
251
463
|
|
252
|
-
|
253
|
-
|
464
|
+
command = COMPUTER_MDM_COMMANDS[command]
|
465
|
+
cmd_rsrc = "#{COMPUTER_MDM_RSRC}/#{command}"
|
254
466
|
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
# the hash key used for the JSON list output of all objects in the JSS
|
262
|
-
RSRC_LIST_KEY = :computers
|
263
|
-
|
264
|
-
# The hash key used for the JSON object output.
|
265
|
-
# It's also used in various error messages
|
266
|
-
RSRC_OBJECT_KEY = :computer
|
467
|
+
if COMPUTER_MDM_COMMANDS_NEEDING_PASSCODE.include? command
|
468
|
+
unless passcode && passcode.is_a?(String) && passcode.length == 6
|
469
|
+
raise JSS::MissingDataError, "Command '#{command}' requires a 6-character passcode"
|
470
|
+
end
|
471
|
+
cmd_rsrc << "/passcode/#{passcode}"
|
472
|
+
end
|
267
473
|
|
268
|
-
|
269
|
-
# DEPRECATED, with be removed in a future release.
|
270
|
-
VALID_DATA_KEYS = [:sus, :distribution_point, :alt_mac_address].freeze
|
474
|
+
targets = JSS.to_s_and_a(targets.to_s)[:arrayform] unless targets.is_a? Array
|
271
475
|
|
272
|
-
|
273
|
-
|
476
|
+
# make sure its an array of ids
|
477
|
+
targets.map! do |comp|
|
478
|
+
if all_ids.include? comp.to_i
|
479
|
+
comp.to_i
|
480
|
+
elsif all_names.include? comp
|
481
|
+
map_all_ids_to(:name).invert[comp]
|
482
|
+
else
|
483
|
+
raise JSS::NoSuchItemError, "No computer found matching '#{comp}'"
|
484
|
+
end # if
|
485
|
+
end # map!
|
274
486
|
|
275
|
-
|
276
|
-
SEARCH_CLASS = JSS::AdvancedComputerSearch
|
487
|
+
cmd_rsrc << "/id/#{targets.join ','}"
|
277
488
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
#
|
282
|
-
BOOT_FLAG = ' (Boot Partition)'.freeze
|
283
|
-
|
284
|
-
# file uploads can send attachments to the JSS using :computers as the sub-resource.
|
285
|
-
UPLOAD_TYPES = { attachment: :computers }.freeze
|
286
|
-
|
287
|
-
# A mapping of Symbols available to the send_mdm_command class method, to
|
288
|
-
# the String commands actuallly sent via the API.
|
289
|
-
COMPUTER_MDM_COMMANDS = {
|
290
|
-
blank_push: 'BlankPush',
|
291
|
-
send_blank_push: 'BlankPush',
|
292
|
-
blank: 'BlankPush',
|
293
|
-
noop: 'BlankPush',
|
294
|
-
device_lock: 'DeviceLock',
|
295
|
-
lock: 'DeviceLock',
|
296
|
-
lock_device: 'DeviceLock',
|
297
|
-
erase_device: 'EraseDevice',
|
298
|
-
erase: 'EraseDevice',
|
299
|
-
wipe: 'EraseDevice',
|
300
|
-
unmanage_device: 'UnmanageDevice',
|
301
|
-
unmanage: 'UnmanageDevice'
|
302
|
-
}.freeze
|
489
|
+
result = JSS::API.post_rsrc cmd_rsrc, nil
|
490
|
+
result =~ %r{<command_uuid>(.*)</command_uuid>}
|
491
|
+
Regexp.last_match(1)
|
492
|
+
end # send mdm command
|
303
493
|
|
304
494
|
# Attributes
|
305
495
|
#####################################
|
@@ -526,7 +716,7 @@ module JSS
|
|
526
716
|
# As well as :id and :name, computers can be queried using :udid, :serialnumber, and :mac_address
|
527
717
|
#
|
528
718
|
def initialize(args = {})
|
529
|
-
super args
|
719
|
+
super args
|
530
720
|
|
531
721
|
# now we have raw @init_data with something in it, so fill out the instance vars
|
532
722
|
@alt_mac_address = @init_data[:general][:alt_mac_address]
|
@@ -621,6 +811,234 @@ module JSS
|
|
621
811
|
@software[:licensed_software]
|
622
812
|
end
|
623
813
|
|
814
|
+
# Get application usage data for this computer
|
815
|
+
# for a given date range.
|
816
|
+
#
|
817
|
+
# @param start_date [String,Date,DateTime,Time]
|
818
|
+
#
|
819
|
+
# @param end_date [String,Date,DateTime,Time] Defaults to start_date
|
820
|
+
#
|
821
|
+
# @return [Hash{Date=>Array<Hash>}] For each day in the range, an Array
|
822
|
+
# with one Hash per application used. The hash keys are:
|
823
|
+
# :name => String, the name of the app
|
824
|
+
# :version => String ,the version of the app
|
825
|
+
# :foreground => Integer, the minutes it was in the foreground
|
826
|
+
# :open => Integer, the minutes it was running.
|
827
|
+
#
|
828
|
+
def application_usage(start_date, end_date = nil)
|
829
|
+
end_date ||= start_date
|
830
|
+
start_date = Time.parse start_date if start_date.is_a? String
|
831
|
+
end_date = Time.parse end_date if end_date.is_a? String
|
832
|
+
unless ([start_date.class, end_date.class] - APPLICATION_USAGE_DATE_CLASSES).empty?
|
833
|
+
raise JSS::InvalidDataError, 'Invalid Start or End Date'
|
834
|
+
end
|
835
|
+
start_date = start_date.strftime APPLICATION_USAGE_DATE_FMT
|
836
|
+
end_date = end_date.strftime APPLICATION_USAGE_DATE_FMT
|
837
|
+
data = JSS.api_connection.get_rsrc(APPLICATION_USAGE_RSRC + "/id/#{@id}/#{start_date}_#{end_date}")
|
838
|
+
parsed_data = {}
|
839
|
+
data[APPLICATION_USAGE_KEY].each do |day_hash|
|
840
|
+
date = Date.parse day_hash[:date]
|
841
|
+
parsed_data[date] = day_hash[:apps]
|
842
|
+
end
|
843
|
+
parsed_data
|
844
|
+
end # app usage
|
845
|
+
|
846
|
+
# The 'computer management' data for this computer, looked up on the fly.
|
847
|
+
#
|
848
|
+
# Without specifying a subset:, the entire dataset is returned as a hash of
|
849
|
+
# arrays, one per subset
|
850
|
+
# If a subset is given then only that array is returned, and it contains
|
851
|
+
# hashes with data about each item (usually :name and :id)
|
852
|
+
#
|
853
|
+
# If the only: param is provided with a subset, it is used as a hash-key to
|
854
|
+
# map the array to just those values, so subset: :smart_groups, only: :name
|
855
|
+
# will return an array of names of smartgroups that contain this computer.
|
856
|
+
#
|
857
|
+
# @param subset[Symbol] Fetch only a subset of data, as an array.
|
858
|
+
# must be one of the symbols in MGMT_DATA_SUBSETS
|
859
|
+
#
|
860
|
+
# @param only[Symbol] When fetching a subset, only return one value
|
861
|
+
# per item in the array. meaningless without a subset.
|
862
|
+
#
|
863
|
+
# @param refresh[Boolean] should the data be re-cached from the API?
|
864
|
+
#
|
865
|
+
# @return [Hash] Without a subset:, a hash of all subsets, each of which is
|
866
|
+
# an Array
|
867
|
+
#
|
868
|
+
# @return [Array] With a subset:, an array of items in that subset.
|
869
|
+
#
|
870
|
+
def management_data(subset: nil, only: nil, refresh: false)
|
871
|
+
@management_data ||= {}
|
872
|
+
if subset
|
873
|
+
management_data_subset(subset: subset, only: only, refresh: refresh)
|
874
|
+
else
|
875
|
+
full_management_data refresh
|
876
|
+
end
|
877
|
+
end
|
878
|
+
|
879
|
+
def full_management_data(refresh = false)
|
880
|
+
@management_data[:full] = nil if refresh
|
881
|
+
return @management_data[:full] if @management_data[:full]
|
882
|
+
mgmt_rsrc = MGMT_DATA_RSRC + "/id/#{@id}"
|
883
|
+
@management_data[:full] = JSS.api.get_rsrc(mgmt_rsrc)[MGMT_DATA_KEY]
|
884
|
+
@management_data[:full]
|
885
|
+
end
|
886
|
+
private :full_management_data
|
887
|
+
|
888
|
+
def management_data_subset(subset: nil, only: nil, refresh: false)
|
889
|
+
raise "Subset must be one of :#{MGMT_DATA_SUBSETS.join ', :'}" unless MGMT_DATA_SUBSETS.include? subset
|
890
|
+
@management_data[subset] = nil if refresh
|
891
|
+
return @management_data[subset] if @management_data[subset]
|
892
|
+
subset_rsrc = MGMT_DATA_RSRC + "/id/#{@id}/subset/#{subset}"
|
893
|
+
@management_data[subset] = JSS.api.get_rsrc(subset_rsrc)[MGMT_DATA_KEY]
|
894
|
+
return @management_data[subset] unless only
|
895
|
+
@management_data[subset].map { |d| d[only] }
|
896
|
+
end
|
897
|
+
private :management_data_subset
|
898
|
+
|
899
|
+
# A shortcut for 'management_data subset: :smart_groups'
|
900
|
+
#
|
901
|
+
def smart_groups(only: nil, refresh: false)
|
902
|
+
management_data subset: :smart_groups, only: only, refresh: refresh
|
903
|
+
end
|
904
|
+
|
905
|
+
# A shortcut for 'management_data subset: :static_groups'
|
906
|
+
#
|
907
|
+
def static_groups(only: nil, refresh: false)
|
908
|
+
management_data subset: :static_groups, only: only, refresh: refresh
|
909
|
+
end
|
910
|
+
|
911
|
+
# A shortcut for 'management_data subset: :policies'
|
912
|
+
#
|
913
|
+
def policies(only: nil, refresh: false)
|
914
|
+
management_data subset: :policies, only: only, refresh: refresh
|
915
|
+
end
|
916
|
+
|
917
|
+
# A shortcut for 'management_data subset: :os_x_configuration_profiles'
|
918
|
+
#
|
919
|
+
def configuration_profiles(only: nil, refresh: false)
|
920
|
+
management_data subset: :os_x_configuration_profiles, only: only, refresh: refresh
|
921
|
+
end
|
922
|
+
|
923
|
+
# A shortcut for 'management_data subset: :ebooks'
|
924
|
+
#
|
925
|
+
def ebooks(only: nil, refresh: false)
|
926
|
+
management_data subset: :ebooks, only: only, refresh: refresh
|
927
|
+
end
|
928
|
+
|
929
|
+
# A shortcut for 'management_data subset: :mac_app_store_apps'
|
930
|
+
#
|
931
|
+
def app_store_apps(only: nil, refresh: false)
|
932
|
+
management_data subset: :mac_app_store_apps, only: only, refresh: refresh
|
933
|
+
end
|
934
|
+
|
935
|
+
# A shortcut for 'management_data subset: :restricted_software'
|
936
|
+
#
|
937
|
+
def restricted_software(only: nil, refresh: false)
|
938
|
+
management_data subset: :restricted_software, only: only, refresh: refresh
|
939
|
+
end
|
940
|
+
|
941
|
+
# A shortcut for 'management_data subset: :patch_reporting_software_titles'
|
942
|
+
#
|
943
|
+
def patch_titles(only: nil, refresh: false)
|
944
|
+
management_data subset: :patch_reporting_software_titles, only: only, refresh: refresh
|
945
|
+
end
|
946
|
+
|
947
|
+
# Return this computer's history.
|
948
|
+
# WARNING! Its huge, better to use a subset a
|
949
|
+
# nd one of the shortcut methods.
|
950
|
+
#
|
951
|
+
# @param subset[Symbol] the subset to return, rather than full history.
|
952
|
+
#
|
953
|
+
# @param refresh[Boolean] should we re-cache the data from the API?
|
954
|
+
#
|
955
|
+
# @return [Hash] The full history
|
956
|
+
#
|
957
|
+
# @return [Array] The history subset requested
|
958
|
+
#
|
959
|
+
def history(subset: nil, refresh: false)
|
960
|
+
@history ||= {}
|
961
|
+
if subset
|
962
|
+
history_subset(subset: subset, refresh: refresh)
|
963
|
+
else
|
964
|
+
full_history refresh
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
def full_history(refresh = false)
|
969
|
+
@history[:full] = nil if refresh
|
970
|
+
return @history[:full] if @history[:full]
|
971
|
+
history_rsrc = HISTORY_RSRC + "/id/#{@id}"
|
972
|
+
@history[:full] = JSS.api.get_rsrc(history_rsrc)[HISTORY_KEY]
|
973
|
+
@history[:full]
|
974
|
+
end
|
975
|
+
private :full_history
|
976
|
+
|
977
|
+
def history_subset(subset: nil, refresh: false)
|
978
|
+
raise "Subset must be one of :#{HISTORY_SUBSETS.join ', :'}" unless HISTORY_SUBSETS.include? subset
|
979
|
+
@history[subset] = nil if refresh
|
980
|
+
return @history[subset] if @history[subset]
|
981
|
+
subset_rsrc = HISTORY_RSRC + "/id/#{@id}/subset/#{subset}"
|
982
|
+
@history[subset] = JSS.api.get_rsrc(subset_rsrc)[HISTORY_KEY]
|
983
|
+
@history[subset]
|
984
|
+
end
|
985
|
+
private :history_subset
|
986
|
+
|
987
|
+
# Shortcut for history(:computer_usage_logs)
|
988
|
+
def usage_logs(refresh = false)
|
989
|
+
history(subset: :computer_usage_logs, refresh: refresh)
|
990
|
+
end
|
991
|
+
|
992
|
+
# Shortcut for history(:audits)
|
993
|
+
def audits(refresh = false)
|
994
|
+
history(subset: :audits, refresh: refresh)
|
995
|
+
end
|
996
|
+
|
997
|
+
# Shortcut for history(:policy_logs)
|
998
|
+
def policy_logs(refresh = false)
|
999
|
+
history(subset: :policy_logs, refresh: refresh)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
# Shortcut for history(:policy_logs), but just the completed policies
|
1003
|
+
def completed_policies(refresh = false)
|
1004
|
+
policy_logs(refresh).select { |pl| pl[:status] == POLICY_STATUS_COMPLETED }
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
# Shortcut for history(:policy_logs), but just the failes policies
|
1008
|
+
def failed_policies(refresh = false)
|
1009
|
+
policy_log(refresh).select { |pl| pl[:status] == POLICY_STATUS_FAILED }
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
# Shortcut for history(:casper_remote_logs)
|
1013
|
+
def casper_remote_logs(refresh = false)
|
1014
|
+
history(subset: :casper_remote_logs, refresh: refresh)
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# Shortcut for history(:screen_sharing_logs)
|
1018
|
+
def screen_sharing_logs(refresh = false)
|
1019
|
+
history(subset: :screen_sharing_logs, refresh: refresh)
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
# Shortcut for history(:casper_imaging_logs)
|
1023
|
+
def casper_imaging_logs(refresh = false)
|
1024
|
+
history(subset: :casper_imaging_logs, refresh: refresh)
|
1025
|
+
end
|
1026
|
+
|
1027
|
+
# Shortcut for history(:commands)
|
1028
|
+
def commands(refresh = false)
|
1029
|
+
history(subset: :commands, refresh: refresh)
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
# Shortcut for history(:user_location)
|
1033
|
+
def user_location_history(refresh = false)
|
1034
|
+
history(subset: :user_location, refresh: refresh)
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# Shortcut for history(:mac_app_store_applications)
|
1038
|
+
def app_store_app_history(refresh = false)
|
1039
|
+
history(subset: :mac_app_store_applications, refresh: refresh)
|
1040
|
+
end
|
1041
|
+
|
624
1042
|
# Set or unset management acct and password for this computer
|
625
1043
|
#
|
626
1044
|
# @param name[String] the name of the management acct.
|
@@ -629,7 +1047,7 @@ module JSS
|
|
629
1047
|
#
|
630
1048
|
# @return [void]
|
631
1049
|
#
|
632
|
-
# The changes will need to be pushed to the server with #update
|
1050
|
+
# The changes will need to be pushed to the server with {#update}
|
633
1051
|
# before they take effect.
|
634
1052
|
#
|
635
1053
|
# CAUTION: this does nothing to confirm the name and password
|
@@ -656,10 +1074,8 @@ module JSS
|
|
656
1074
|
def make_unmanaged
|
657
1075
|
return nil unless managed?
|
658
1076
|
set_management_to(nil, nil)
|
659
|
-
|
660
|
-
|
661
|
-
rescue
|
662
|
-
end
|
1077
|
+
return unless mdm_capable
|
1078
|
+
self.class.send_mdm_command(@id, :unmanage_device)
|
663
1079
|
end
|
664
1080
|
|
665
1081
|
#
|
@@ -751,34 +1167,47 @@ module JSS
|
|
751
1167
|
@software = nil
|
752
1168
|
end # delete
|
753
1169
|
|
754
|
-
#
|
755
|
-
#
|
756
|
-
#
|
757
|
-
#
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
#
|
765
|
-
#
|
766
|
-
#
|
767
|
-
#
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
#
|
775
|
-
#
|
776
|
-
#
|
777
|
-
#
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
1170
|
+
# Send a blank_push MDM command
|
1171
|
+
#
|
1172
|
+
# See JSS::Computer.send_mdm_command
|
1173
|
+
#
|
1174
|
+
def blank_push
|
1175
|
+
self.class.send_mdm_command @id, :blank_push
|
1176
|
+
end
|
1177
|
+
alias noop blank_push
|
1178
|
+
alias send_blank_push blank_push
|
1179
|
+
|
1180
|
+
# Send a device_lock MDM command
|
1181
|
+
#
|
1182
|
+
# See JSS::Computer.send_mdm_command
|
1183
|
+
#
|
1184
|
+
def device_lock(passcode)
|
1185
|
+
self.class.send_mdm_command @id, :device_lock, passcode
|
1186
|
+
end
|
1187
|
+
alias lock device_lock
|
1188
|
+
alias lock_device device_lock
|
1189
|
+
|
1190
|
+
# Send an erase_device MDM command
|
1191
|
+
#
|
1192
|
+
# See JSS::Computer.send_mdm_command
|
1193
|
+
#
|
1194
|
+
def erase_device(passcode)
|
1195
|
+
self.class.send_mdm_command @id, :erase_device, passcode
|
1196
|
+
end
|
1197
|
+
alias erase erase_device
|
1198
|
+
alias wipe erase_device
|
1199
|
+
|
1200
|
+
# Remove MDM management profile without
|
1201
|
+
# un-enrolling from the JSS or
|
1202
|
+
# resetting the JSS management acct.
|
1203
|
+
#
|
1204
|
+
# To do those things as well, see {#make_unmanaged}
|
1205
|
+
#
|
1206
|
+
# See JSS::Computer.send_mdm_command
|
1207
|
+
#
|
1208
|
+
def remove_mdm_profile
|
1209
|
+
self.class.send_mdm_command(@id, :unmanage_device)
|
1210
|
+
end
|
782
1211
|
|
783
1212
|
# aliases
|
784
1213
|
alias alt_macaddress alt_mac_address
|
@@ -830,7 +1259,7 @@ module JSS
|
|
830
1259
|
computer << purchasing_xml if has_purchasing?
|
831
1260
|
|
832
1261
|
doc.to_s
|
833
|
-
end
|
1262
|
+
end # rest_xml
|
834
1263
|
|
835
1264
|
end # class Computer
|
836
1265
|
|